如何提升PHP生成CSV文件的速度?现有实现运行过慢求优化方案
CSV生成慢的优化方案
当前代码的核心性能瓶颈是N+1次SQL查询:外层查询一次订单列表后,每遍历一个订单都要单独执行2次SQL查询客户信息和订单商品明细,若查询到1000个订单,就会产生2001次数据库请求,大量时间浪费在PHP和数据库的通信开销上。可按以下优先级优化:
1. 合并SQL查询,消灭N+1问题
用JOIN语句把三张表的查询合并为1次,一次性获取所有需要的字段,大幅减少数据库交互次数。
参考合并后的SQL:
SELECT o.ordr_timedate, o.ordr_id, o.ordr_cust_id, o.ordr_b_company, o.ordr_shipping_selected, c.cust_gl_code, oc.orcn_prod_code, oc.orcn_prod_part_num, oc.orcn_prod_name, oc.orcn_prod_type, oc.orcn_option_0, oc.orcn_price, oc.orcn_qty, oc.orcn_total FROM shop_orders o LEFT JOIN shop_customers c ON o.ordr_cust_id = c.cust_id LEFT JOIN shop_orders_content oc ON o.ordr_id = oc.orcn_ordr_id WHERE o.ordr_ship_id = {$sid} AND o.ordr_timedate BETWEEN {$strdate} AND {$enddate} ORDER BY o.ordr_timedate DESC
调整后只需要遍历一次SQL查询结果,直接组装CSV行即可,无需循环内再查数据库。
2. 添加数据库索引,加速查询
给涉及的查询字段加索引,避免全表扫描:
- 给
shop_orders表添加联合索引:INDEX idx_ship_date (ordr_ship_id, ordr_timedate),覆盖WHERE筛选和ORDER BY排序逻辑,同时可以把需要查询的订单字段加到索引里做成覆盖索引,避免回表查询 - 给
shop_orders_content表添加索引:INDEX idx_ordr_id (orcn_ordr_id),加速订单和商品明细的关联查询 shop_customers表的cust_id如果不是主键/没有索引,也要单独添加
3. PHP层优化,减少IO开销
- 如果生成CSV是用于用户下载,不要写入本地磁盘,直接输出到
php://output流,省掉磁盘写入开销,示例代码:
// 直接输出到浏览器,不需要生成本地文件 header('Content-Type: text/csv; charset=utf-8'); header('Content-Disposition: attachment; filename=orders-by-supplier.csv'); $file = fopen('php://output', 'w');
- 读取查询结果时使用
mysqli_fetch_assoc代替mysqli_fetch_array,避免生成无用的数字索引数组,减少内存占用 - 不要把所有查询结果加载到内存后再写CSV,采用边查边写的流式处理,降低内存峰值
4. 超大数据量分批处理
如果符合条件的记录超过10万条,可以采用分批查询的方式:每次用LIMIT查询1000~5000条记录,处理完后再查下一批,避免单次查询占用过多数据库内存和PHP内存,同时保证处理速度。
内容的提问来源于stack exchange,提问作者Noob Coder
相关产品推荐
相关产品推荐

