开发经常遇到这样的场景产品王:想做一个后台导出自定义时间范围订单信息的功能。事不宜迟,开发小哥用了半天就完成了功能并上线了。结果第二天产品王一来上班就拍桌子:MD,我想把去年全年的订单全部导出,结果后台直接挂了!开发小哥查了一下,原来是内存溢出了,一年的订单量是1000万。于是,开发小哥就跟产品王吵架了:你又不是傻子,你导出1000W的数据去做,你要挂服务器吗?于是,产品王和程序狗就这样架起了一座桥梁。但。产品的需求就这么敢回去,万一老板提出需求,非要导出1000万条记录,你还得乖乖回去码字怎么办?那么,如果真的要导出这么大的数据,该怎么办呢?在开发中,我们经常会使用框架来提高我们的开发效率,但这也意味着框架会封装一些数据。比如Yii2,当我们要获取去年的订单时,我们的代码会这样写:Order::find()->where("create_timebetween'2016-01-01'AND'2016-12-31'")->所有();当我们遍历上面代码得到的结果集时,如果数据量很大,内存就会溢出。原因是:在Yii2中,all方法被阻塞,数组中存储了大量的数据,遍历大数据必然导致内存快速增加。那么如何取出大量数据而不存储在数组中呢?这里用到了PHP中的迭代器:Iterator。如果你看过PDO::query的返回值类型,就会发现这个方法返回的PDOStatement是Iterator的实现。关于Iterator,请各位自行脑补。既然框架已经为我们做了冗余的封装,那么我们就使用原生的API来实现吧。以下是完整代码$sql='select*fromuser';$pdo=new\PDO('mysql:host=127.0.0.1;dbname=test','root','root');$pdo->setAttribute(\PDO::MYSQL_ATTR_USE_BUFFERED_QUERY,false);$rows=$pdo->query($sql);$filename=date('Ymd').'.csv';//设置文件名header('Content-Type:text/csv');header("Content-Disposition:attachment;filename={$filename}");$out=fopen('php://output','w');fputcsv($out,['id','username','password','create_time']);foreach($rowsas$row){$line=[$row['id'],$row['username'],$row['password'],$row['create_time']];fputcsv($out,$line);}fclose($out);$memory=round((memory_get_usage()-$startMemory)/1024/1024,3).'M'.PHP_EOL;file_put_contents('/tmp/test.txt',$内存,FILE_APPEND);在表中生成7位记录,执行上面的代码,通过查看内存使用情况发现整个过程只占用了0.XM,完全没有内存溢出现象。
