读取/解析大量JSON.gz文件的优化技巧

3 投票
1 回答
2880 浏览
提问于 2025-04-20 23:21

我遇到了一个有趣的问题。作为一个刚开始接触数据处理的初学者,我希望能从这里的老手们得到一些建议。

我大约有 6000 个 Json.gz 文件,压缩后总共大约 5GB,解压后大约 20GB。我正在打开每个文件,逐行读取,使用 gzip 模块;然后用 json.loads() 来加载每一行,并解析复杂的 JSON 结构。接着,我会把每个文件的行一次性插入到 Pytable 中,然后再处理下一个文件。

这一切大约花了我 3 个小时。批量插入到 Pytable 并没有提高速度。大部分时间都花在从解析后的 JSON 行中获取值上,因为它们的结构实在太糟糕了。有些结构比较简单,比如 'attrname':attrvalue,但有些则复杂且耗时,比如:

'attrarray':[{'name':abc, 'value':12},{'value':12},{'name':xyz, 'value':12}...]

...在这里,我需要提取 attr 数组中所有对象的 value,这些对象有对应的 name,而那些没有的则要忽略。所以我需要遍历这个列表,检查每个 JSON 对象。如果你能指出任何更快的聪明方法,我会很感激。

所以我觉得实际的解析部分可能没有太多提升速度的空间。我认为可能有提升速度的空间是在读取文件的部分。

我做了一些测试(现在手头没有具体数据),即使在去掉解析部分后,单纯逐行读取文件本身也花了相当多的时间。

所以我想问:在这个问题中,你觉得我有没有做得不够优化的地方?

for filename in filenamelist:
    f = gzip.open(filename):
    toInsert=[]
    for line in f:
        parsedline = json.loads(line)
        attr1 = parsedline['attr1']
        attr2 = parsedline['attr2']
        .
        .
        .
        attr10 = parsedline['attr10']
        arr = parsedline['attrarray']
        for el in arr:
            try:
                if el['name'] == 'abc':
                    attrABC = el['value']
                elif el['name'] == 'xyz':
                    attrXYZ = el['value']
                .
                .
                .
            except KeyError:
                pass
        toInsert.append([attr1,attr2,...,attr10,attrABC,attrXYZ...])

    table.append(toInsert)

1 个回答

3

一个简单的建议

如果你需要反复访问同样的压缩文件(从你的描述来看,这似乎不是一次性的操作),那么你应该只解压一次,而不是每次读取时都解压。

解压缩是一个对CPU要求很高的操作,而Python的gzip模块相比于C语言的工具如zcat/gunzip来说,速度并不是特别快

最快的办法可能是先用gunzip把所有文件解压,结果保存到某个地方,然后在你的脚本中直接读取这些解压后的文件。

其他问题

接下来的内容并不是直接的答案,但太长了不适合放在评论里。为了提高速度,你需要考虑几个其他问题:

  1. 你想用这些数据做什么
  2. 你真的需要一次性加载所有数据吗?
    • 如果你能把数据分成小块,那么可以减少程序的延迟,虽然整体所需时间可能不会减少。例如,你可能只需要从特定文件中提取几行特定的数据来进行分析……太好了!只加载那些特定的行。
    • 如果你确实需要以任意和不可预测的方式访问数据,那么你应该把它加载到另一个系统中(比如RDBMS?),这样可以以更适合你分析的数据格式来存储。

如果最后一点是正确的,一个选择是把每个JSON“文档”加载到PostgreSQL 9.3数据库中(JSON支持非常强大,而且速度也很快),然后从那里进行进一步的分析。希望你在加载时能从JSON文档中提取出有意义的键。

撰写回答