【问题标题】:oursql extremely slow in inserting data我们的sql插入数据非常慢
【发布时间】:2012-06-15 15:05:58
【问题描述】:

我正在尝试将 python 脚本生成的一些数据存储在 MySQL 数据库中。基本上我使用的是命令:

con = oursql.connect(user="user", host="host", passwd="passwd", 
                     db="testdb")
c = con.cursor()                             

c.executemany(insertsimoutput, zippedsimoutput)

con.commit()
c.close()

在哪里,

insertsimoutput = '''insert into simoutput 
                        (repnum, 
                         timepd, 
                         ...) values (?, ?, ...?)'''

大约插入了 30,000 行,大约有 15 列。以上大约需要7分钟。如果我使用 MySQLdb 而不是 oursql,大约需要 2 秒。为什么会有这么大的差异?这应该在oursql中以其他方式完成吗,我们的oursql只是很慢?如果有更好的方法用我们的sql插入这些数据,如果你能告诉我,我将不胜感激。

谢谢。

【问题讨论】:

  • 有没有看过oursql正在做的查询?
  • @Simeon 我不知道如何找到它。你能解释一下吗?
  • 您可以查看mysql server logs。根据您观察到的时间差,我假设我们的 sqls executemany 执行 30k 单行插入,而 MySQLdb 一次插入多行。

标签: python mysql database oursql


【解决方案1】:

不同之处在于 MySQLdb 对您的查询进行了一些黑客攻击,而我们的 SQL 则没有...

接受这个:

cursor.executemany("INSERT INTO sometable VALUES (%s, %s, %s)",
    [[1,2,3],[4,5,6],[7,8,9]])

MySQLdb 在运行之前将其翻译成这样:

cursor.execute("INSERT INTO sometable VALUES (1,2,3),(4,5,6),(7,8,9)")

但如果你这样做:

cursor.executemany("INSERT INTO sometable VALUES (?, ?, ?)",
    [[1,2,3],[4,5,6],[7,8,9]])

在我们的sql中,它被翻译成类似这样的伪代码:

stmt = prepare("INSERT INTO sometable VALUES (?, ?, ?)")
for params in [[1,2,3],[4,5,6],[7,8,9]]:
    stmt.execute(*params)

因此,如果您想模拟 mysqldb 正在做什么,但要从准备好的语句和 oursql 的其他优点中受益,您需要这样做:

from itertools import chain
data = [[1,2,3],[4,5,6],[7,8,9]]
one_val = "({})".format(','.join("?" for i in data[0]))
vals_clause = ','.join(one_val for i in data)
cursor.execute("INSERT INTO sometable VALUES {}".format(vals_clause),
    chain.from_iterable(data))

我敢打赌,如果你这样做,oursql 会更快 :-)

此外,如果您认为它丑陋,那您是对的。但请记住 MySQL db 在内部做了一些更丑陋的事情 - 它使用正则表达式来解析您的 INSERT 语句并中断参数化部分,然后执行我建议您为 oursql 做的事情。

【讨论】:

    【解决方案2】:

    我会说检查oursql 是否支持bulk insert sql 命令以提高性能。

    【讨论】:

    • 我在他们的文档中没有找到任何关于 bulk insert 的信息。谢谢!
    【解决方案3】:

    Oursql 确实支持bulk insert 语句。为此,我使用 sqlalchemy 包装器编写了代码。

    对于纯oursql,这样的应该没问题:

    with open('tmp.csv', 'wb') as tmp:
        for item in zippedsimoutput:
            tmp.write("{0}\n".format(item))
    c.execute("""LOAD DATA LOCAL INFILE 'tmp.csv' INTO TABLE flags FIELDS TERMINATED BY ',' ENCLOSED BY '"'  LINES TERMINATED BY '\r\n' ;""")
    

    请注意,行的顺序必须与数据库中的列的顺序相同。

    【讨论】:

    • 转储到 csv 然后加载看起来不像 @Curious2learn 想要的......
    • @underrun 已经有几年了,但是 IIRC 这是我们的sql 支持创建bulk insert 的唯一方式。由于他正在寻找一种快速插入大型数据集的方法,因此演示批量插入语法似乎最有用。同样,IIRC,oursql 支持多次插入,这就是 orm 不转换查询,而是对每个项目运行多次的原因。由于没有多重插入,剩下的唯一选择是批量插入。这对底层 MySQL 数据库来说不是问题,但这不是问题。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-12-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多