【问题标题】:Faster Insertion of Records into a Table with SQLAlchemy使用 SQLAlchemy 更快地将记录插入到表中
【发布时间】:2011-02-22 08:07:27
【问题描述】:

我正在解析日志并使用 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,插入时间是一个多小时。数据库引擎之间的时间差似乎是正确的,还是意味着我做错了什么?

【问题讨论】:

  • 我还应该说明,当我想知道一种加速它的方法时,我的意思是我应该做一些基本的事情,而不是会给我带来很大的收获(即,在至少超过 25% 的时间速度增加)。速度在这里不是最重要的,我只是想知道我是否在以一种行人的方式做某事,因为这对我来说是全新的。

标签: python sql mysql sqlite sqlalchemy


【解决方案1】:

在不了解表引擎(MyISAM?InnoDB?)、架构和索引的情况下,很难评论您在那里使用的两个数据库之间的细节。

但是,当像这样使用 MySQL 时,您可能会发现将数据写入临时文本文件然后use the LOAD DATA INFILE syntax 将其全部加载到数据库中要快得多。看起来像 you can call the execute method on your connection object 运行执行此操作所需的 SQL。

此外,如果您一心想要逐行添加内容,并且每次都在重新创建表,您可以验证程序中的关键约束并仅在插入所有行后添加这些约束,从而节省数据库对每个插入进行约束检查的时间。

【讨论】:

  • "您可以验证程序中的关键约束,并仅在插入所有行后添加这些约束,从而节省数据库对每次插入进行约束检查的时间。"你能帮我把它分解一下吗,那部分超出了我的想象:-P
  • @Kyle 如果没有可以使用的表模式,很难给出细节。但是,例如,如果您有任何 UNIQUE 索引,则该唯一性是对表的约束。每次插入一行时,数据库都会确保没有另一行与该行冲突。由于您只有一张表,因此您不必担心外键约束,但如果您以后要添加它们,这也适用于它们。
【解决方案2】:

您应该尝试的最重要的事情是围绕多个插入进行事务,因为将数据库提交到磁盘确实需要很长时间。您需要确定批处理级别,但粗略的第一次尝试是围绕整个批次包装交易。

【讨论】:

  • 那么类似于我创建的ins对象数组,然后在数组满时执行?或者这不是你的意思?
  • @Kyle:您需要使用 transaction = session.create_transaction(); 创建一个事务;然后再做一个 transaction.commit 。请参阅rmunn.com/sqlalchemy-tutorial/tutorial.html 并向下滚动到“交易”标题。
  • 实际上并不是我所做的,但这是我所做的背后的想法,并且喜欢给人们加分:-)。解释了我在下面做了什么。
【解决方案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 之间的区别是什么。

我找到了这个here的基本信息。

结果:
引擎:以分钟为单位的非分组插入时间:以分钟为单位的分组插入时间
Sqlite:61:8
MySql:15:2.5

我没有刷新可能有源文本文件的 mysql 和 sqlite 之间的缓存,但我认为这不会是一个相对显着的差异。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-09-10
    • 2015-01-29
    • 2021-10-15
    • 1970-01-01
    • 2021-01-31
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多