广告广告位招商联系 ads@iiunion.comA5 · 728×90(移动端 320×100) · 参考价 ¥300/月了解详情 →

PHP 数组大数据处理内存爆炸?生成器与流式读取实战

Started by Internet_Notes · 2026-11-06 01:06 UTC · 8 views · 2 replies
I Member
PHP 处理大文件时最常见的事故就是内存爆炸:一个几十万行的 CSV 读进数组,内存直接冲到上限,进程被 OOM 干掉。最近重构一个数据导入脚本时踩了一遍,记录一下实测取舍。

一、别把全量数据装进数组
最常见的写法是 file() 一把读进来再 foreach,文件一大就爆。改成 SplFileObject 逐行读,或者用 fgets 循环,内存占用就从几百 MB 降到几 MB。实测一个 800MB 的日志文件,逐行处理峰值内存不到 20MB。

二、生成器是处理大数据的主力
需要对数据做转换时,用 yield 写生成器:函数内部逐条产出,调用方逐条消费,中间不产生大数组。对比实测:同样处理 50 万行数据,数组写法峰值内存 1.2GB,生成器写法不到 30MB,速度还略快一点,因为少了数组扩容的开销。

三、注意生成器的几个坑
生成器不能反复遍历,第二次 foreach 会直接返回空;需要复用时要重新调用函数生成。还有一个容易忽略的点:生成器内部如果持有数据库连接或文件句柄,提前 break 时要记得在 finally 里释放,否则长驻进程里句柄会泄漏。

四、内存上限不是解决方案
调大 memory_limit 只是把爆炸推迟,不是解决。我的原则是:单次请求处理的数据量超过 10 万行就必须走流式,超过 100 万行还要考虑分批落库、断点续跑,避免一次超时全丢。

大家在 PHP 里处理大数据时还有什么省内存的招数?欢迎分享。
I Editorial
大文件别file()进数组、改用逐行/生成器,内存从GB掉到MB是实打实的。补充:生成器不能二次遍历,需要时就重新打开源或把中间结果落到磁盘/队列;json_decode大文档同样会爆,能流式解析就别整包。批处理写库时注意ORM实体累积,flush/clear要按批,否则生成器省下的内存又被实体管理器吃回去。用同一数据集对比峰值内存和耗时再定批次大小。
I Member
补充一点实践细节:ORM 批量导入时只靠生成器还不够,identity map 会一直攒实体,必须每处理比如 500 条就 flush 加 clear,否则内存还是阶梯上涨,生成器省下的内存又被 ORM 吃回去。CSV 建议用 SplFileObject 逐行读,别用 file_get_contents;大 JSON 可以用流式解析器按节点读,json_decode 一次性解码几百 MB 文档基本必爆。另外生成器只能遍历一次,需要两次统计时别复用同一个生成器,重新打开源文件或把聚合结果先落盘,否则第二次遍历直接为空,排查时很容易误以为是数据源问题。
广告广告位招商联系 ads@iiunion.comA6 · 728×90(移动端 320×100) · 参考价 ¥200/月了解详情 →
Log in or register to reply.