1.背景

将大量数据百万级导出到csv。由于数据量大,仅扩大内存仍会报错。

百万级的数据从数据库中查询,若不分段查或不刷新缓冲区,均会出错。

2.方案

分段从数据库中查询再定期刷新缓冲区的方法。

set_time_limit(0);//让程序一直运行
ini_set('memory_limit', '128M');//设置临时内存

$fileName = '测试导出数据';//导出文件名
header('Content-Encoding: UTF-8');
header("Content-type:application/vnd.ms-excel;charset=UTF-8");
header('Content-Disposition: attachment;filename="' . $fileName . '.csv"');
$fp = fopen('php://output', 'a');
fwrite($fp, chr(0xEF).chr(0xBB).chr(0xBF));//bom防乱码
fputcsv($fp, ['title1', 'title2', 'title3']);//表头

require_once '../class/db.class.php';
$obj=new db();
$sqlBefore="select id from setting_item where 1 ";
$count=$obj->getNumRow($sqlBefore);
$nums=10000;//每次导出数量
$step=ceil($count/$nums);//循环次数
for($i = 0; $i < $step; $i++) {
  $start = $i * 10000;
  $sql=str_replace("id", "*", $sqlBefore)." LIMIT {$start},{$nums}";
  //echo $sql;
  $q=$obj->getAll3($sql);
  while($item=$q->fetch_array(MYSQLI_USE_RESULT)){
  //print_r($item);
    fputcsv($fp, $item);
  }
  //每1万条数据就刷新缓冲区
  ob_flush();
  flush();
}//for

3.结论

13万条数据,导出时间7s。

4.转文本

默认不是文本,如001会被存在1,6/7会被保存成6月7日等,解决方案是统一保存成文本格式,导出变量加\t,如下:

$item["size"]="\t".$item["size"];