百万级数据导出
数据导出的瓶颈在于数据查询,数据的条数查询大量消耗内存,导致服务宕机,所以按照主键id来分批查询数据
/** * 百万级数据导出 */ public function excelout(){ //不限制执行时间,以防超时 set_time_limit(0); //文件名 $xlsName = iconv('utf-8', 'gb2312', 'user');//文件名称 //统计总行数 $sqlCount = 0; //表头 $xlsCell = ['id','手机号','时间']; //对应表头的字段 $fields = 'id,phone,create_time'; //查询数据库中最大的id号,然后根据号分批导出 $sqlCount = db('webuser')->max('id'); //每次取多少条 $sqlLimit = 50000;//每次只从数据库取2000条 // buffer计数器 $cnt = 0; $fileNameArr = array(); //分段执行,以免内存写满 for ($i = 0; $i < ceil($sqlCount / $sqlLimit); $i++) { $pathFilename = ROOT_PATH.DS.'public'.DS.DS.'excel'.DS.$xlsName; $fp = fopen($pathFilename . '_' . $i . '.csv', 'w'); //生成临时文件 $fileNameArr[] = $pathFilename . '_' . $i . '.csv';//将临时文件保存起来 // 第一次执行时将表头写入 if($i == 0){ fputcsv($fp, $xlsCell); } $startId = $i * $sqlLimit; $endId = $startId + $sqlLimit; //查询出数据 $xlsData = db('webuser')->field($fields) ->where('phone','<>', '') ->where('id',['<',$endId],['>=',$startId],'and') // ->fetchSql() ->select(); // dump($xlsData); foreach ($xlsData as $k=>$v) { $v['create_time'] = date('Y-m-d H:i:s',$v['create_time']); $cnt++; //执行下一次循环之前清空缓冲区 if ($sqlLimit == $cnt) { ob_flush(); $cnt = 0; } //每行写入到临时文件 fputcsv($fp, $v); } fclose($fp); //每生成一个文件关闭 } //将所有临时文件合并成一个 foreach ($fileNameArr as $file){ //如果是文件,提出文件内容,写入目标文件 if(is_file($file) && filesize($file) != 0 ){ $fileName = $file; //打开临时文件 $handle1 = fopen($fileName,'r'); //读取临时文件 if($str = fread($handle1,filesize($fileName))){ //关闭临时文件 fclose($handle1); //打开或创建要合并成的文件,往末尾插入的方式添加内容并保存 $handle2 = fopen($xlsName.'.csv','a+'); //写入内容 if(fwrite($handle2, $str)){ //关闭合并的文件,避免浪费资源 fclose($handle2); } } } } //将文件压缩,避免文件太大,下载慢 $zip = new \ZipArchive(); $filename = $xlsName . ".zip"; $zip->open($filename, \ZipArchive::CREATE); //打开压缩包 $zip->addFile($xlsName.'.csv', basename($xlsName.'.csv')); //向压缩包中添加文件 $zip->close(); //关闭压缩包 foreach ($fileNameArr as $file) { unlink($file); //删除csv临时文件 } //输出压缩文件提供下载 header("Cache-Control: max-age=0"); header("Content-Description: File Transfer"); header('Content-disposition: attachment; filename=' . basename($filename)); // 文件名 header("Content-Type: application/zip"); // zip格式的 header("Content-Transfer-Encoding: binary"); // header('Content-Length: ' . filesize($filename)); // @readfile($filename);//输出文件; unlink($filename); //删除压缩包临时文件 unlink($xlsName.'.csv'); //删除合并的临时文件 }