使用jq处理50GB超大JSON文件时内存耗尽的问题排查与解决
解决大JSON文件内存耗尽问题:提取data数组元素逐行输出
你的问题核心在于第一个jq命令会把整个data数组重新构建成完整的JSON对象后再传递给第二个jq,这直接导致50GB的文件被全部加载到内存中,自然触发内存耗尽。咱们来拆解问题,给出高效的流处理方案:
为什么原命令会失效?
先看你的原命令:
jq -cn --stream 'fromstream(1|truncate_stream(inputs))' file.json | jq -cr .data[] >> out.json
1|truncate_stream(inputs)会去掉所有流数据的路径第一层(也就是移除"data"前缀),随后fromstream会把这些零散的流数据重新组装成完整的data数组对象——这一步完全违背了--stream的设计初衷,它会把整个数组加载到内存里,直接引发内存溢出。- 后续管道到第二个jq提取
.data[]时,第一个jq已经把庞大数据读进内存,崩溃是必然的。
正确的流处理命令
我们可以直接在流模式下筛选并输出data数组的每个元素,不需要先构建整个数组。用下面的单步命令即可:
jq -cn --stream ' fromstream( inputs | select(.[0][0] == "data" and (.[0] | length) == 2) | .[0] |= .[1:] )' file.json >> out.json
命令逐段解释:
--stream:开启流处理模式,逐块读取JSON,全程不会把整个文件加载到内存inputs:获取所有流输入(每个输入是[路径, 值]的数组,比如[["data",0,"id"], "id1"])select(.[0][0] == "data" and (.[0] | length) == 2):只保留路径第一层是"data"且路径长度为2的流数据——这对应的是data数组里的单个完整元素(比如路径["data",0]对应整个{"id":"id1","value":"value1"}对象的所有流片段).[0] |= .[1:]:把路径从["data",0]修改为[0],让fromstream能正确把这些流片段重建为单个独立对象-c:以紧凑格式输出每个对象,正好满足你逐行输出的需求
这个命令会逐个处理data数组里的元素,处理完一个就输出一个,内存占用始终维持在极低水平,完全不会出现内存耗尽的问题。
额外优化点
原命令用两个jq进程通过管道传递数据,会产生额外的IO开销。上面的单进程命令直接完成所有操作,效率更高。
内容的提问来源于stack exchange,提问作者Dhanushka Amarakoon
相关产品推荐
相关产品推荐

