处理大型(10GB+)文本文件的最佳遍历方式是什么?

5 投票
2 回答
2921 浏览
提问于 2025-04-17 05:28

我需要处理一个非常大的文本文件,大小有好几个GB(具体来说是一个区域文件)。我需要对区域文件中的每一条记录运行几个查询,然后把结果存储到一个可以搜索的数据库里。

目前我选择的工具主要是因为我熟悉它们,分别是Python和MySQL。不过,我不太确定这两个工具能否处理这么大的文件。

有没有在这方面有经验的人能给我一些建议,怎样才能打开并遍历这个文件而不让我的系统崩溃?还有,一旦我能打开文件,处理文件的最有效方法是什么(比如使用多线程)以及如何存储处理后的数据?

2 个回答

0

@Zack Bloom的回答非常棒,我给了他点赞。这里有几点我的想法:

  • 正如他所展示的,使用 with open(filename) as f:for line in f 就足够了。open() 函数会返回一个迭代器,每次给你一行文件内容。

  • 如果你想把每一行都放进数据库,就在循环里做。如果你只想要某些符合特定规则的行,那也很简单。

    import re

    pat = re.compile(some_pattern)

    with open(filename) as f:
        for line in f:
            if not pat.search(line):
                continue
            # do the work to insert the line here
  • 如果文件有好几个GB大,你可能会遇到输入输出的瓶颈。所以其实没必要担心多线程之类的。即使运行几个正则表达式,处理数据的速度也可能比读取文件或更新数据库还快。

  • 就我个人而言,我对数据库不是很在行,喜欢用ORM(对象关系映射)。我上一个做数据库的项目中,用的是Autumn和SQLite。我发现ORM的默认设置是每插入一条记录就提交一次,这样插入很多记录会非常慢。所以我对Autumn进行了扩展,让你可以把多条插入操作放在一个提交中;这样速度快多了。(嗯,我应该让Autumn支持Python的 with 语句,这样你可以把多条插入放在一个 with 块里,Autumn会自动提交。)

http://autumn-orm.org/

总之,我想说的是,处理数据库时,如果方法不对可能会非常慢。如果你发现插入数据库是个瓶颈,可能有办法解决这个问题,而Zack Bloom的回答里有几个不错的建议可以帮助你入手。

6

在MySQL中存储这么多数据其实没什么大问题,不过你可能无法把整个数据库都放在内存里,所以要做好可能会有一些输入输出性能问题的准备。像往常一样,运行查询之前一定要确保你有合适的索引。

最重要的是,不要试图把整个文件一次性加载到内存中。应该逐行读取文件,而不是用像readlines这样的方式,这样会一次性把整个文件都加载进来。

确保请求是批量处理的。一次加载几千行,然后把它们一起发送一个大的SQL请求。

这种方法应该有效:

def push_batch(batch):
    # Send a big INSERT request to MySQL

current_batch = []
with open('filename') as f:
    for line in f:
        batch.append(line)

        if len(current_batch) > 1000:
            push_batch(current_batch)
            current_batch = []

    push_batch(current_batch)

区域文件的格式通常比较规范,可以考虑直接使用LOAD DATA INFILE。你也可以考虑创建一个命名管道,从Python中把部分格式化的数据推送进去,然后用LOAD DATA INFILE从MySQL中读取。

MySQL有一些很好的建议来优化插入速度,这里有一些重点:

  • 在每个插入语句中使用多个值列表。
  • 使用INSERT DELAYED,特别是当你同时从多个客户端推送数据时(比如使用线程)。
  • 在插入之前锁定你的表。
  • 调整key_buffer_size和bulk_insert_buffer_size。

最快的处理会在MySQL中完成,所以考虑一下是否可以在数据进入数据库后再进行你需要的查询,而不是在之前。如果你确实需要在Python中进行操作,使用线程也不会有帮助。因为在同一时间内,只有一个Python线程可以执行(这叫做GIL),所以除非你在做一些耗时的C语言操作,或者与外部资源交互,否则你最终也只会在一个线程中运行。

最重要的优化问题是是什么限制了速度,如果数据库是瓶颈,那就没必要启动很多线程去读取文件。真正知道的方法就是尝试一下,进行调整,直到速度满足你的需求。

撰写回答