普通循环不适合大规模JSON数组是因为会一次性加载全部数据导致内存溢出;应使用Generator流式解析JSON Lines格式,或自定义Iterator分块处理标准JSON数组,并结合检查点与异常捕获实现可控、可重试的百万级处理。

为什么普通循环不适合大规模JSON数组
当JSON数据量超过几万条,或单个文件体积超5MB时,用file_get_contents()一次性加载再json_decode(),极易触发内存溢出(OOM)。PHP默认内存限制常为128MB,而解码后的PHP数组实际占用可能是原始JSON的3–5倍。更严重的是,foreach遍历前必须把整个结构载入内存,无法“边读边处理”。
用Generator实现流式逐条解析
不将全部JSON载入内存,而是按需生成每一条记录——这是最实用的进阶方案。适用于JSON Lines格式(每行一个合法JSON对象):
- 用
fopen()打开文件,配合fgets()逐行读取,每行独立调用json_decode($line, true) - 在函数中使用
yield返回解码后的数组,让调用方以foreach消费,但底层不囤积数据 - 示例函数结构:
function parseJsonLines($filePath) {<br> $handle = fopen($filePath, 'r');<br> while (($line = fgets($handle)) !== false) {<br> $line = trim($line);<br> if (empty($line)) continue;<br> $data = json_decode($line, true);<br> if (json_last_error() === JSON_ERROR_NONE && is_array($data)) {<br> yield $data;<br> }<br> }<br> fclose($handle);<br>}
调用时:foreach (parseJsonLines('big.jsonl') as $item) { /* 处理单条 */ },内存占用稳定在几百KB级别。
立即学习“PHP免费学习笔记(深入)”;
自定义Iterator类处理嵌套数组场景
若JSON是标准数组格式[{...},{...},...]但体量巨大,可封装一个支持“分块拉取”的迭代器:
- 继承
Iterator接口,内部维护文件指针和当前偏移位置 - 利用
json_decode()的$depth参数与JSON_BIGINT_AS_STRING防止整数截断或栈溢出 - 关键技巧:不依赖全文解析,改用正则或状态机定位每个
{...}边界(需确保JSON无换行内嵌字符串),提取子串后单独解码 - 避免
array_walk_recursive()——它仍要求完整结构在内存中
结合缓存与中断恢复提升鲁棒性
长时间运行的批量处理可能被中断。加入检查点机制:
- 每次成功处理N条后,将当前行号或字节偏移写入临时文件
- 重启时先读取该偏移,用
fseek($handle, $offset)跳转,避免重头开始 - 对每条记录加
try/catch包裹,捕获json_decode失败但不终止整体流程,记录错误行供人工核查
这种模式让百万级JSON处理变得可控、可追踪、可重试。



















