zoukankan      html  css  js  c++  java
  • PHP百万级数据导出方案(生成器直接输出单个CSV)优化版

    PHP百万级数据导出方案(生成器直接输出单个CSV)
    原创Tim_PHPer 发布于2019-01-25 17:41:12 阅读数 2905 收藏
    展开
    概述:
    之前写过一篇文章叫PHP百万级数据导出方案(多csv文件压缩),发现很多朋友都很感兴趣,但是当时用的方法比较不方便,可能不太符合很多人的需求。后来想了一下如何优化时,了解到能用生成器来处理内存溢出更方便,所以当时文章中也补充分享了一下这想法。然而,发现更多朋友对如何结合生成器导出数据感兴趣,因此这篇文章,我来填下坑。

    准备:
    1、了解生成器yield
    我上篇文章PHP百万级数据导出方案(多csv文件压缩) 说了几个坑,大家可以先去回顾一下。然后我们可以看一下前辈的对生成器介绍的文章: PHP性能优化利器:生成器 yield理解,来理解一下生成器防止内存溢出的原理。

    2、解决数据查询内存溢出
    了解完生成器yield原理之后,我们还需要解决一个问题,我们常用的mysql查询函数是mysqli_query(connection,query,resultmode);,通常都是直接填写第一第二个参数就直接查询,但该函数默认的是对全部结果集进行缓存,这会导致数据过多的时候,内存也会溢出。因此,我们需要设置第三个参数为MYSQLI_USE_RESULT来逐行读取结果集。那或许您有疑问为什么不直接 遍历该mysqli_query()方法返回的结果来当每一行数据写到CSV中,而还要用yield来存储一次每行数据再写到CSV中呢?其实这是因为mysqli_query()返回的结果是mysqli_result Object形式,而fputcsv()这个方法要求第二个参数为数组。所以yield的作用就是中转站,但是他是一行行运输数据,而不是读多行来运输数据。

    代码:
    <?php

    /*
    * 该方法是把数据库读出的数据进行CSV文件输出,能接受百万级别的数据输出,因为用生成器,不用担心内存溢出。
    * @param string $sql 需要导出的数据SQL
    * @param string $mark 生成文件的名字前缀
    *
    */
    function putCsv($sql, $mark)
    {
    set_time_limit(0);
    header('Content-Type: application/vnd.ms-excel;charset=utf-8');
    header('Content-Disposition: attachment;filename="' . $mark . '"');
    header('Cache-Control: max-age=0');

    $file_num = 0; //文件名计数器
    $fileNameArr = array();
    $fp = fopen($mark .'_'.$file_num .'.csv', 'w'); //生成临时文件
    $fileNameArr[] = $mark .'_'.$file_num .'.csv';
    fwrite($fp, chr(0xEF).chr(0xBB).chr(0xBF));//转码,防止乱码
    foreach (query($sql) as $a) {
    fputcsv($fp, $a);
    }
    fclose($fp); //每生成一个文件关闭
    }


    //生成器来缓存mysql查询结果,返回类型为数组
    function query($sql){
    $con = mysqli_connect("localhost", "root", "root");
    if (!$con) {
    die('Could not connect: ' . mysqli_error());
    }
    mysqli_select_db($con, "dbmigs_smz");
    mysqli_query($con,'set names utf8');
    $n = 0;
    // print_r(mysqli_query($con, $sql,MYSQLI_USE_RESULT) ); mysqli_result Object 返回
    //该处用MYSQLI_USE_RESULT 就是不缓存结果集中,也是为了避免内存溢出,相当于mysql_unbuffered_query
    foreach (mysqli_query($con, $sql,MYSQLI_USE_RESULT) as $row ){ //
    yield $row;
    echo $n++ . "<br>"; //输出行数
    }
    $con->close();
    }

    //$sql = 'SELECT id,ArticleClassify,ArticleTitle FROM `test_yield` where id < 1200000';
    $sql = 'SELECT id,ArticleClassify,ArticleTitle FROM `test_yield` where id < 20';
    $mark = 'test';
    putCsv($sql,$mark,1,2);

    ?>
    总结:
    其实上面代码默认单个CSV文件,可以改变参数来输出多个CSV文件,大家可以根据需求修改,除此之外,建议多个CSV文件可以打包成一个压缩包来方便处理,具体可以参照我第一篇文章的代码。

    以上便是本人对PHP大数据导出的见解,希望能帮到您们,同时不足的地方请多多指教!

    PS:非常抱歉,之前复制demo代码的时候大意没有把putcsv这个自定义的方法复制给大家看,导致有部分读者有疑惑,现在已经补齐demo
    ————————————————
    版权声明:本文为CSDN博主「Tim_PHPer」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
    原文链接:https://blog.csdn.net/Tim_phper/article/details/86636608

  • 相关阅读:
    innerHTML获取标签内容
    images获取所有图片标签
    getElementsByTagName通过标签名获取元素对象集合
    getElementsByName通过标签的name属性来获取元素对象
    getElementsByClassName通过类名获取元素对象集合
    getElementById通过id找到对象
    tp3.2查询指定的字段
    流程控制语句if、else、elif、break、continue
    Python3基础知识之元组、集合、字典
    快速自定义输出格式
  • 原文地址:https://www.cnblogs.com/jenkin1991/p/12167705.html
Copyright © 2011-2022 走看看