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

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS PostgreSQL,高可用系列 2核4GB
RDS MySQL Serverless 高可用系列,价值2615元额度,1个月
简介: 一、PHP迭代生成器1.PHP性能优化利器:迭代生成器 yield理解很多PHP开发者或许都不知道生成器这个功能,可能是因为生成器是PHP 5.5.0才引入的功能,也可以是生成器作用不是很明显。但是,生成器功能的确非常有用。

一、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;


三、实战一(100w数据导出1个csv文件,在浏览器下载)


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);
?>
复制代码


四、实战二(100w数据导出多个csv文件,在文件夹中保存)


<?php
/*
 * 该方法是把数据库读出的数据进行CSV文件输出,能接受百万级别的数据输出,因为用生成器,不用担心内存溢出。
 * @param string $sql 需要导出的数据SQL
 * @param string $mark 生成文件的名字前缀
 *
 */
function putCsv($sql, $mark)
{
    set_time_limit(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));//转码,防止乱码
    //添加导出标题
    fputcsv($fp, ['id', 'type_id', 'state','addtime','usetime']);
    foreach (query($sql) as $a) {
        fputcsv($fp, $a);
    }
    fclose($fp);  //每生成一个文件关闭
}
//生成器来缓存mysql查询结果,返回类型为数组
function query($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();
}
//数据库总数据
$count = 1000000;
//文件名称
$mark = 'test';
//一个excel保存的条数
$limit = 500000;
$page = 1;
$i = 0;
do
{
    $i+= $limit;
    //计算偏移量
    $start = ($page-1)*$limit;
    $sql = "SELECT id,type_id,state,addtime,usetime FROM `shop_ercode_records` limit {$start}".','."{$limit}";
    putCsv($sql,$mark.$i);
    $page++;
} while ($i<$count);
?>
复制代码


五、总结


  1. 浏览器只能同时下载一个文件,循环导出,浏览器最后智能下载最后一个文件
  2. 导出的数据到csv能打开查看的只到100w行,如果超过100w行,进行导出多个csv文件


相关实践学习
每个IT人都想学的“Web应用上云经典架构”实战
本实验从Web应用上云这个最基本的、最普遍的需求出发,帮助IT从业者们通过“阿里云Web应用上云解决方案”,了解一个企业级Web应用上云的常见架构,了解如何构建一个高可用、可扩展的企业级应用架构。
MySQL数据库入门学习
本课程通过最流行的开源数据库MySQL带你了解数据库的世界。 &nbsp; 相关的阿里云产品:云数据库RDS MySQL 版 阿里云关系型数据库RDS(Relational Database Service)是一种稳定可靠、可弹性伸缩的在线数据库服务,提供容灾、备份、恢复、迁移等方面的全套解决方案,彻底解决数据库运维的烦恼。 了解产品详情:&nbsp;https://www.aliyun.com/product/rds/mysql&nbsp;
相关文章
|
3月前
|
JSON 数据处理 PHP
PHP数组处理技巧:高效操作数据集合
PHP数组处理技巧:高效操作数据集合
|
3月前
|
JSON 安全 大数据
PHP中的数组处理艺术:灵活高效的数据操作
PHP中的数组处理艺术:灵活高效的数据操作
|
3月前
|
JSON 定位技术 PHP
PHP技巧:解析JSON及提取数据
这就是在PHP世界里探索JSON数据的艺术。这场狩猎不仅仅是为了获得数据,而是一种透彻理解数据结构的行动,让数据在你的编码海洋中畅游。通过这次冒险,你已经掌握了打开数据宝箱的钥匙。紧握它,让你在编程世界中随心所欲地航行。
142 67
|
6月前
|
数据库连接 PHP 数据库
【YashanDB知识库】PHP使用ODBC驱动无法获取长度为256char以上的数据
【YashanDB知识库】PHP使用ODBC驱动无法获取长度为256char以上的数据
|
6月前
|
Oracle 关系型数据库 MySQL
【YashanDB知识库】php查询超过256长度字符串,数据被截断的问题
本文分析了YashanDB中PHP通过ODBC查询数据时出现的数据截断问题,表现为超过256字节的数据被截断,以及isql工具无法显示超过300字节长度的数据。问题根源在于YashanDB的ODBC驱动仅支持单次查询,且PHP扩展库默认缓冲区限制。解决方案包括改用PHP ODBC扩展库而非PDO_ODBC,以及调整isql代码逻辑以支持循环取数或一次性读取完整数据。文章还提供了具体代码示例和规避方法,适用于23.2.4.14及更早版本。
【YashanDB知识库】php查询超过256长度字符串,数据被截断的问题
|
7月前
|
Oracle 关系型数据库 MySQL
【YashanDB 知识库】php 查询超过 256 长度字符串,数据被截断的问题
php 查询超过 256 字节数据,显示被截断:yashandb 的 odbc 驱动接口 SQLGetData 现在只支持单次查询,不支持多次取数据的操作。 isql 显示不出来,isql 工具最大只查询 300 长度的数据,超过了该长度未正常显示。
|
10月前
|
数据采集 JSON JavaScript
如何通过PHP爬虫模拟表单提交,抓取隐藏数据
本文介绍了如何使用PHP模拟表单提交并结合代理IP技术抓取京东商品的实时名称和价格,特别是在电商大促期间的数据采集需求。通过cURL发送POST请求,设置User-Agent和Cookie,使用代理IP绕过限制,解析返回数据,展示了完整代码示例。
204 3
如何通过PHP爬虫模拟表单提交,抓取隐藏数据
|
10月前
|
缓存 NoSQL PHP
Redis作为PHP缓存解决方案的优势、实现方式及注意事项。Redis凭借其高性能、丰富的数据结构、数据持久化和分布式支持等特点,在提升应用响应速度和处理能力方面表现突出
本文深入探讨了Redis作为PHP缓存解决方案的优势、实现方式及注意事项。Redis凭借其高性能、丰富的数据结构、数据持久化和分布式支持等特点,在提升应用响应速度和处理能力方面表现突出。文章还介绍了Redis在页面缓存、数据缓存和会话缓存等应用场景中的使用,并强调了缓存数据一致性、过期时间设置、容量控制和安全问题的重要性。
179 5
|
10月前
|
XML 前端开发 JavaScript
PHP与Ajax在Web开发中的交互技术。PHP作为服务器端脚本语言,处理数据和业务逻辑
本文深入探讨了PHP与Ajax在Web开发中的交互技术。PHP作为服务器端脚本语言,处理数据和业务逻辑;Ajax则通过异步请求实现页面无刷新更新。文中详细介绍了两者的工作原理、数据传输格式选择、具体实现方法及实际应用案例,如实时数据更新、表单验证与提交、动态加载内容等。同时,针对跨域问题、数据安全与性能优化提出了建议。总结指出,PHP与Ajax的结合能显著提升Web应用的效率和用户体验。
217 3
|
10月前
|
存储 SQL 数据处理
PHP中如何高效地处理大规模数据的排序?
在PHP中处理大规模数据排序时,选择合适的方法至关重要。对于内存内可以处理的数据,可以直接使用PHP内置排序函数;对于超出内存限制的数据,可以考虑分块处理、外部排序或利用数据库的排序功能。根据具体应用场景和数据规模,选择最佳的排序策略,确保排序操作高效且稳定。
96 2