如何用jq读取100+GB的文件而不耗尽内存》

问题描述 投票:0回答:3

我有一个 100+GB 的 json 文件,当我尝试用 jq 读取它时,我的计算机继续运行我们的 ram。有没有办法在限制内存使用的同时读取文件,或者有其他方法来读取非常大的 json 文件?

我在命令中输入的内容:

jq 'keys' fileName.json

json bigdata jq
3个回答
2
投票

确定包含单个 JSON 实体的非常大文件的结构的一种通用方法是运行以下查询:

jq -nc --stream -f structural-paths.jq huge.json | sort -u

其中

structural_paths.jq
包含:

inputs
| select(length == 2)
| .[0]
| map( if type == "number" then 0 else . end )

请注意,输出中的“0”表示相应位置至少有一个有效的数组索引,而不是“0”实际上是该位置的有效索引。

另请注意,对于非常大的文件,使用 jq --stream 处理整个文件可能会非常慢。

示例:

给定

{"a": {"b": [0,1, {"c":2}]}}
,上述咒语的结果将是:

["a","b",0,"c"]
["a","b",0]

顶层结构

如果您只是想了解有关顶层结构的更多信息,您可以将上面的 jq 程序简化为:

inputs | select(length==1)[0][0] | if type == "number" then 0 else . end

结构达到给定深度

如果命令行

sort
失败,那么您可能希望通过仅将路径考虑到一定深度来限制路径数量。

如果深度不是太大,那么希望你的命令行

sort
能够管理;如果没有,那么使用命令行
uniq
至少会稍微修剪输出。

更好的选择可能是在 jq 中定义

unique(stream)
,然后使用它,如下所示:

# Output: a stream of the distinct `tostring` values of the items in the stream
def uniques(stream):
  foreach (stream|tostring) as $s ({};
     if .[$s] then .emit = false else .emit = true | .item = $s | .[$s]=true end;
     if .emit then .item else empty end );

def spaths($depth):
  inputs
  | select(length==1)[0][0:$depth]
  | map(if type == "number" then 0 else . end);

uniques(spaths($depth))

jq 的合适调用将如下所示:

jq -nr --argjson depth 3 --stream -f structural-paths.jq huge.json

除了避免排序成本之外,使用

uniques/1
还将保留原始 JSON 中路径的顺序。

“JSON 指针”指针

如果要将数组路径表达式转换为“JSON Pointer”字符串(例如与

jm
jstream
一起使用),只需将以下内容附加到相关的 jq 程序中:

| "/" + join("/")

2
投票

jq 的流解析器(使用 --stream 选项调用)通常可以处理非常非常大的文件(甚至在满足某些条件的情况下可以处理任意大的文件),但它通常非常慢并且通常非常麻烦。

在实践中,我发现诸如 jstream 和/或我自己的 jm 之类的工具在处理巨大文件时与 jq 结合使用效果非常好。 当以这种方式使用时,它们都非常易于使用,尽管安装可能有点麻烦。

不幸的是,如果您对 JSON 文件的内容一无所知,除了

jq empty
花费太长时间或失败之外,那么据我所知,没有任何 CLI 工具可以自动生成有用的模式。然而,查看文件的前几个字节通常会提供足够的信息来开始。或者您可以从
jm count
开始给出顶级对象的计数,然后从那里开始。如果顶级是 JSON 对象,
jm -s | jq 'keys[]'
将为您提供顶级键的列表。


这是一个例子。假设我们确定文件 ginormous.json 的大小主要是因为它由一个非常长的顶级数组组成。然后假设 schema.jq (已在本页其他地方提到)在 pwd 中,您有希望通过运行找到信息丰富的模式:

jm ginormous.json |
  jq -n 'include "schema" {source:"."}; schema(inputs)'

另请参阅 jq 以递归方式分析 JSON 对象,以获得更简单的模式推理引擎。


1
投票

我在这里发布了一个相关问题:slurp、空输入和输入过滤器之间的区别

如果您的文件很大,但文件中的文档不是那么大(只有很多较小的文档),

jq -n 'inputs'
可以帮助您开始:

jq -n 'inputs | keys'

这是一个示例(带有小文件):

$ jq -n 'inputs | keys' <<JSON
{"foo": 21, "bar": "less interesting data"}
{"foo": 42, "bar": "more interesting data"}
JSON
[
  "bar",
  "foo"
]
[
  "bar",
  "foo"
]

如果您有一个千兆字节大或具有数百万个键的顶级对象,则此方法将不起作用。

© www.soinside.com 2019 - 2024. All rights reserved.