使用SQLAlchemy加快记录插入到表中的速度

5 投票
3 回答
5142 浏览
提问于 2025-04-15 22:59

我正在解析一个日志文件,并使用SQLAlchemy和Python将数据插入到MySQL或SQLite数据库中。现在,我打开数据库连接,然后在遍历每一行时,将解析后的数据插入(目前只是一个大表,对SQL不太熟悉)。当循环结束后,我再关闭连接。总结一下代码就是这样:

log_table = schema.Table('log_table', metadata,
                         schema.Column('id', types.Integer, primary_key=True),
                         schema.Column('time', types.DateTime),
                         schema.Column('ip', types.String(length=15))
....
engine = create_engine(...)
metadata.bind = engine
connection = engine.connect()
....
for line in file_to_parse:
    m = line_regex.match(line)
    if m:
        fields = m.groupdict()
        pythonified = pythoninfy_log(fields) #Turn them into ints, datatimes, etc
        if use_sql:
            ins = log_table.insert(values=pythonified)
            connection.execute(ins)
            parsed += 1

我有两个问题:

  • 有没有办法在这个基本框架下加快插入的速度?比如说,使用一个插入队列和一些插入线程,或者进行批量插入之类的?
  • 我在使用MySQL时,大约插入120万条记录花了15分钟。而使用SQLite时,插入时间超过了一个小时。这个数据库引擎之间的时间差是否正常,还是说我做错了什么?

3 个回答

3

我做了以下操作来实现一些批处理:

inserts = []
insert_every = 1000
for line in file_to_parse:
    m = line_regex.match(line)
    if m:
        fields = m.groupdict()
        if use_sql: #This uses Globals, Ick :-/
            inserts.append(pythonified)
            if (parsed % insert_every) == 0:
                connection.execute(log_table.insert(), inserts)
                inserts = []
            parsed += 1
if use_sql:
    if len(inserts) > 0:
        connection.execute(log_table.insert(), inserts)

这个方法没有使用事务,但以一种非常懒惰的方式让我把插入和解析的时间从大约13秒缩短到大约2秒,使用的是mysql数据库,样本也比较小。我现在会看看在这个改动下,使用完整样本时mysql和sqlite之间的差别。

我在这里找到了这个的基本信息:这里

结果:
引擎:非分组插入时间(分钟):分组插入时间(分钟)
Sqlite: 61: 8
MySql: 15: 2.5

在mysql和sqlite之间我没有清空缓存,这样可能会影响源文本文件,但我认为这不会造成比较大的差别。

3

在不知道你用的数据库表引擎(是MyISAM还是InnoDB?)、表结构和索引的情况下,很难具体评论你使用的两个数据库之间的差异。

不过,当你这样使用MySQL时,你会发现把数据先写到一个临时的文本文件里,然后再用LOAD DATA INFILE语法把这些数据加载到数据库里,通常会快得多。看起来你可以在你的连接对象上调用执行方法来运行必要的SQL语句来完成这个操作。

另外,如果你坚持要一行一行地添加数据,并且每次都在重新创建表的话,你可以在程序中验证关键约束条件,并在所有行都插入后再添加这些约束,这样可以节省数据库在每次插入时检查约束的时间。

4

你可以尝试把多个插入操作放在一个事务里,因为把数据保存到硬盘上是最耗时的部分。你需要决定一下批处理的级别,但一个简单的初步尝试就是把所有操作都放在一个事务里。

撰写回答