PHP迭代生成器秒级导出500万数据

一、PHP迭代生成器

1.PHP性能优化利器:迭代生成器 yield理解

很多PHP开发者或许都不知道生成器这个功能,可能是因为生成器是PHP 5.5.0才引入的功能,也可以是生成器作用不是很明显。但是,生成器功能的确非常有用。

2.迭代生成器的优点

直接讲概念估计你听完还是一头雾水,所以我们先来说说优点,也许能勾起你的兴趣。那么迭代生成器有哪些优点,如下:

  • 生成器会对PHP应用的性能有非常大的影响
  • PHP代码运行时节省大量的内存
  • 比较适合计算大量的数据

二、快速生成百万条测试数据

这里推荐数据库的蠕虫复制命令
先建好表后,插2条测试数据后,执行几次下列命令,即可指数增长

INSERT INTO user (username,phone,city) SELECT username,phone,city FROM user;

三、实战

1.解决数据查询内存溢出
了解完生成器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 exportCsv($sql,$fileName)
{
    //让程序一直运行
    set_time_limit(0);
//设置程序运行内存
    ini_set('memory_limit', '128M');

    header('Content-Encoding: UTF-8');
    header("Content-type:application/vnd.ms-excel;charset=UTF-8");
    header('Content-Disposition: attachment;filename="' . $fileName . '.csv"');

    //打开php标准输出流
    $fp = fopen('php://output', 'a');

    //添加BOM头,以UTF8编码导出CSV文件,如果文件头未添加BOM头,打开会出现乱码。
    fwrite($fp, chr(0xEF).chr(0xBB).chr(0xBF));

    //添加导出标题
    fputcsv($fp, ['id', 'username', 'phone','city']);

    foreach (getExportData($sql) as $item) {
        //向csv表格中添加每一行数据
        fputcsv($fp, $item);
    }
    fclose($fp);  //每生成一个文件关闭
}


/**
 * 获取要导出的数据
 * 使用迭代生成器来缓存mysql查询结果,返回类型为数组
 * @param $sql
 * @return Generator
 */
function getExportData($sql){
    //连接数据库
    $con = mysqli_connect("localhost", "root", "");
    //连接数据库报错信息
    if (!$con) {
        die('Could not connect: ' . mysqli_error());
    }
    mysqli_select_db($con, "demo");
    mysqli_query($con,'set names utf8');
    //该处用MYSQLI_USE_RESULT 就是不缓存结果集中,也是为了避免内存溢出,相当于mysql_unbuffered_query
    foreach (mysqli_query($con, $sql,MYSQLI_USE_RESULT) as $row ){  //
        yield $row;
    }
    $con->close();
}

//要导出的数据
$sql = 'SELECT id,username,phone,city FROM `user`  where id < 1000000';
$mark = 'export';
exportCsv($sql,$mark);

?>
复制代码

四、总结

导出的数据能excel能打开查看的只到100w行,如果超过100w行,可以进行导出多个csv文件

おすすめ

転載: juejin.im/post/7031720791347560484