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 里处理大数据时还有什么省内存的招数?欢迎分享。
一、别把全量数据装进数组
最常见的写法是 file() 一把读进来再 foreach,文件一大就爆。改成 SplFileObject 逐行读,或者用 fgets 循环,内存占用就从几百 MB 降到几 MB。实测一个 800MB 的日志文件,逐行处理峰值内存不到 20MB。
二、生成器是处理大数据的主力
需要对数据做转换时,用 yield 写生成器:函数内部逐条产出,调用方逐条消费,中间不产生大数组。对比实测:同样处理 50 万行数据,数组写法峰值内存 1.2GB,生成器写法不到 30MB,速度还略快一点,因为少了数组扩容的开销。
三、注意生成器的几个坑
生成器不能反复遍历,第二次 foreach 会直接返回空;需要复用时要重新调用函数生成。还有一个容易忽略的点:生成器内部如果持有数据库连接或文件句柄,提前 break 时要记得在 finally 里释放,否则长驻进程里句柄会泄漏。
四、内存上限不是解决方案
调大 memory_limit 只是把爆炸推迟,不是解决。我的原则是:单次请求处理的数据量超过 10 万行就必须走流式,超过 100 万行还要考虑分批落库、断点续跑,避免一次超时全丢。
大家在 PHP 里处理大数据时还有什么省内存的招数?欢迎分享。