背景:接手的项目中支持导出一批数据,全数量在50W左右。在接手的时候看代码是直接一次查询MySQL获得数据,然后用header函数直接写入csv,用户开始导出则自动下载。但是,在全导出的时候,功能出现了BUG问题。

1.数据量大导致PHP处理脚本运行时间,超过默认限制。

2.数据量过大,导致内存溢出,流程中止。

初版解决方案:

1.通过函数set_time_limit(0);       取消执行时间限制(在导出的函数入口设置,这是合理的,导出的数据量过大了)

2.关于数据过大,内存溢出的解决办法,开始是想到了php动态变量(先由sql语句获得总记录数,然后每2W条切分,查询2w条数据存入一个变量)

  1. $total_export_count = $db->getOne("select count(1) from ($sql) t2");
  2. for ($i=0;$i<intval($total_export_count/20000)+1;$i++){
  3. $export_data = "exportdata".$i;
  4. $$export_data = $db->getAll($sql." limit ".strval($i*20000).",20000");
  5. }

然后通过相应的代码取出变量的信息,echo到csv文件中,这种方式在本地测试的时候是通过的,但在服务器上依旧会内存溢出。

  1. header ( "Content-type:application/vnd.ms-excel" );
  2. header ( "Content-Disposition:filename=" . iconv ( "UTF-8", "GB18030", "查询用户列表" ) . ".csv" );
  3. $out = $column_name;
  4. echo iconv ( "UTF-8", "GB18030", $out );
  5. for ($i=0;$i<intval($total_export_count/20000)+1;$i++){
  6. $dynamic_name = "exportdata".$i;
  7. foreach ( $$dynamic_name as $key => $item ) {
  8. echo iconv ( "UTF-8", "GB18030", "\n".implode(',',$item) );
  9. }
  10. // 将已经写到csv中的数据存储变量销毁,释放内存占用
  11. unset($$dynamic_name);
  12. }
  13. exit ();

因为上面的方法在服务器上没有通过,所以只能将分割数据量的操作放到写文件的流程中,相比上面的思路这种会慢一些。

  1. header ( "Content-type:application/vnd.ms-excel" );
  2. header ( "Content-Disposition:filename=" . iconv ( "UTF-8", "GB18030", "查询用户列表" ) . ".csv" );
  3. $out = $column_name;
  4. echo iconv ( "UTF-8", "GB18030", $out );
  5. $pre_count = 20000;
  6. for ($i=0;$i<intval($total_export_count/$pre_count)+1;$i++){
  7. $export_data = $db->getAll($sql." limit ".strval($i*$pre_count).",{$pre_count}");
  8. foreach ( $export_data as $key => $item ) {
  9. echo iconv ( "UTF-8", "GB18030", "\n".implode(',',$item) );
  10. }
  11. // 将已经写到csv中的数据存储变量销毁,释放内存占用
  12. unset($export_data);
  13. }
  14. exit ();

经测试之后是可行的,服务器上也可以导出,就是时间会慢一些,而且会是一直下载状态。

关于这个场景整理了一些资料:

1.csv文件的条数是好像没有限制的,可以一直写(网上的博文里面看的,没证实过)

2.excel 2010版本以上,是可以读取100多W行数据的(验证过,新建一个excel,ctrl+下箭头  到文件末尾可以看到行数)

理想的解决方案(没有具体实施,想的)

1.数据分割肯定是必须的步骤,防止内存溢出。

2.将分割后的数据写入到一个excel或者一个csv文件中,被分割了多少次,写多少个文件。这样可以防止达到文件行数的最大限制。

3.将2中写的文件进行压缩处理,压缩成一个压缩包,然后进行自动下载。

补充:

在上面的方案正式运行的时候发现导出的数据,总是比查询的总记录数要少1000多条,几次看数据后发现有些数据并没有换行,而是写到上一行去了。在有了这个觉悟后,重新看了遍之前转别人的帖子,发现还是用fputcsv()函数比较靠谱,传入一个数组,作为一行的数据,由该函数自己去写换行和控列。感觉有些时候还是不要偷懒的好啊,虽然自己写","完成列分割,写"\n"完成空格,貌似是可行的,但是对于一些数据,并不一定能控制的好。

修改后的导出代码:

  1. header ( "Content-type:application/vnd.ms-excel" );
  2. header ( "Content-Disposition:filename=" . iconv ( "UTF-8", "GB18030", "query_user_info" ) . ".csv" );
  3. // 打开PHP文件句柄,php://output 表示直接输出到浏览器
  4. $fp = fopen('php://output', 'a');
  5. // 将中文标题转换编码,否则乱码
  6. foreach ($column_name as $i => $v) {
  7. $column_name[$i] = iconv('utf-8', 'GB18030', $v);
  8. }
  9. // 将标题名称通过fputcsv写到文件句柄
  10. fputcsv($fp, $column_name);
  11. $pre_count = 10000;
  12. for ($i=0;$i<intval($total_export_count/$pre_count)+1;$i++){
  13. $export_data = $db->getAll($sql." limit ".strval($i*$pre_count).",{$pre_count}");
  14. foreach ( $export_data as $item ) {
  15. $rows = array();
  16. foreach ( $item as $export_obj){
  17. $rows[] = iconv('utf-8', 'GB18030', $export_obj);
  18. }
  19. fputcsv($fp, $rows);
  20. }
  21. // 将已经写到csv中的数据存储变量销毁,释放内存占用
  22. unset($export_data);
  23. ob_flush();
  24. flush();
  25. }
  26. exit ();

php 用csv文件导出大量数据初方案的更多相关文章

  1. [转载] php用csv文件导出大量数据

    header ( "Content-type:application/vnd.ms-excel" ); header ( "Content-Disposition:fil ...

  2. python使用pymongo访问MongoDB的基本操作,以及CSV文件导出

    1. 环境. Python:3.6.1 Python IDE:pycharm 系统:win7 2. 简单示例 import pymongo # mongodb服务的地址和端口号mongo_url = ...

  3. 将CSV文件中的数据导入到SQL Server 数据库中

    导入数据时,需要注意 CSV 文件中的数据是否包含逗号以及双引号,存在时,导入会失败 选择数据库 -> 右键 -> 任务 -> 导入数据 ,然后根据弹出的导入导出向导(如下图)中的提 ...

  4. csv文件批量导入数据到sqlite。

    csv文件批量导入数据到sqlite. 代码: f = web.input(bs_switch = {})  # bs_switch 为from表单file字段的namedata =[i.split( ...

  5. MySQL添加CSV文件中的数据

    一.MySQL添加csv数据 此问题是前几天整理数据的时候碰到的,数据存在 CSV文件中(200多万记录),通过python 往数据库中导入太慢了,后来使用MySQL 中自带的命令 LOAD DATA ...

  6. 基于CentOS的MySQL学习补充四--使用Shell批量从CSV文件里插入数据到数据表

    本文出处:http://blog.csdn.net/u012377333/article/details/47022699 从上面的几篇文章中,能够知道怎样使用Shell创建数据库.使用Shell创建 ...

  7. 【SQL Server数据迁移】把csv文件中的数据导入SQL Server的方法

    [sql] view plaincopy --1.修改系统参数 --修改高级参数 sp_configure 'show advanced options',1 go --允许即席分布式查询 sp_co ...

  8. CSV文件导出2

    public void exportCSVFile( HttpServletResponse response, ResultSet rs,String fileName,String headers ...

  9. csv文件导出

    参考博客:http://www.cnblogs.com/mingforyou/p/4103132.html 导入jar包javacsv.jar 链接:http://pan.baidu.com/s/1i ...

随机推荐

  1. jquery Autocomplete函数

    <!doctype html> <html lang="en"> <head> <meta charset="utf-8&quo ...

  2. Docker for windows 7 - 加载 docker images

    背景 由于之前一直是在 Linux 上面跑,所以对于docker for windows 部分不是很熟. 由于我们的合作伙伴需要在windows 上面跑我们的docker image, 所以在自己的w ...

  3. Java中static关键字介绍

    static关键字主要有两种作用: 第一,为某特定数据类型或对象分配单一的存储空间,而与创建对象的个数无关. 第二,实现某个方法或属性与类而不是对象关联在一起 具体而言,在Java语言中,static ...

  4. 何用glmnet或lars包进行feature selection

    #datalibrary(lars)data(diabetes)attach(diabetes) #glmnetlibrary(glmnet)library(foreach)library(Matri ...

  5. xargs的i参数

    xargs与find经常结合来进行文件操作,平时删日志的时候只是习惯的去删除,比如 # find . -type f -name "*.log" | xargs rm -rf * ...

  6. python学习整理

    Python-copy()与deepcopy()区别 —–我们寻常意义的复制就是深复制,即将被复制对象完全再复制一遍作为独立的新个体单独存在.所以改变原有被复制对象不会对已经复制出来的新对象产生影响. ...

  7. 比XGBOOST更快--LightGBM介绍

    xgboost的出现,让数据民工们告别了传统的机器学习算法们:RF.GBM.SVM.LASSO.........现在,微软推出了一个新的boosting框架,想要挑战xgboost的江湖地位.笔者尝试 ...

  8. Eclipse/jre/jdk/jvm

    理清一下什么是 jre.jdk.jvm这几个容易混淆的东西. 直接在电脑下安装Eclipse,会提醒你缺少JVM. 1.JVM Java Virtual Machine(Java虚拟机)的缩写. 为了 ...

  9. Spring定时器Quartz的使用

    在JavaEE系统中,我们会经常用到定时任务,比如每天凌晨生成前天报表,每一小时生成汇总数据等等,定时更新某某操作……. 我们可以使用java.util.Timer结合java.util.TimerT ...

  10. overflow与underflow

    是新近的firefox浏览器中支持overflow, underflow这两个事件,当某一元素的大小超出父元素的显示范围就会触发overflow事件,如果从超出显示再变回不超出的状态则触发underf ...