2010-10-15 64 views
12

我正在开发一个使用MySQLdb访问MySQL数据库的Python程序。在某些情况下,我必须在许多行上运行INSERT或REPLACE命令。我目前这样做:为什么executemany在Python MySQLdb中变慢?

db.execute("REPLACE INTO " + table + " (" + ",".join(cols) + ") VALUES" + 
    ",".join(["(" + ",".join(["%s"] * len(cols)) + ")"] * len(data)), 
    [row[col] for row in data for col in cols]) 

它工作正常,但它有点尴尬。我想知道如果我能够更容易阅读,并且我发现了有关executemany命令。我改变了我的代码,看起来像这样:

db.executemany("REPLACE INTO " + table + " (" + ",".join(cols) + ") " + 
    "VALUES(" + ",".join(["%s"] * len(cols)) + ")", 
    [tuple(row[col] for col in cols) for row in data]) 

它仍然工作,但它跑了很多慢。在我的测试中,对于相对较小的数据集(约100-200行),它运行速度慢了6倍。对于大数据集(大约13,000行,我希望处理的数据量最大),它的运行速度慢了大约50倍。它为什么这样做?

我真的想简化我的代码,但我不希望性能大幅下降。有谁知道有什么方法可以使其更快?

我使用Python 2.7和MySQLdb 1.2.3。我试着修改setinputsizes函数,但这似乎没有做任何事情。我查看了MySQLdb源代码,看起来它不应该做任何事情。

+0

你插入/替换多少行?你的第二条语句在把它提供给mysql之前会在内存中创建一个巨大的列表 – nosklo 2010-10-15 19:59:08

+1

我正在替换多达13,000行。我认为创建清单并不是瓶颈。如果我创建列表但不将它传递给db光标,它几乎不需要任何时间。 – 2010-10-15 20:14:36

+0

(不会回答这个问题,但是......)'INSERT ... ON DUPLICATE KEY UPDATE ...'几乎总是比'REPLACE ...'更好。 – 2017-05-26 05:24:45

回答

19

尝试在查询中降低“值”一词 - 这似乎是MySQL-python 1.2.3中的错误/回归。

MySQL-python的executemany()实现与正则表达式的VALUES子句相匹配,然后仅克隆每行数据的值列表,因此您最终将执行与第一种方法完全相同的查询。

不幸的是,正则表达式失去了它不区分大小写的标志在该版本中(随后固定在躯干r622但从来没有回迁到1.2分支),因此它会降低到遍历数据,并发射了每行的查询。

+0

我试过了,它的工作原理!对于“值”小写字母,executemany的执行速度与执行速度一样快,或者有时候会更快一些。 – 2010-10-17 00:59:16

+1

请注意,1.2.3正则表达式不能在ON DUPLICATE KEY UPDATE查询中使用参数(正则表达式只匹配第一个参数),所以lower-casing值会导致混淆(因为它们与execute()一起使用)“not在字符串格式化期间转换的所有参数“错误。为避免使用VALUES()格式,而不是查询的ON DUPLICATE KEY部分中的参数。 – 2011-04-12 22:09:00

+0

它已在[1.2.4](https://github.com/farcepest/MySQLdb1/blob/MySQLdb-1.2.4/MySQLdb/cursors.py#L43)中修复。 – saaj 2015-09-27 13:02:06

1

您的第一个示例是生成并发送到数据库的单个(大)语句。

第二个例子是一个简单得多的语句,插入/替换单个行但多次执行。每条命令都单独发送到数据库,因此您必须支付从客户端到服务器的周转时间,并返回插入的每一行。我认为这些命令之间引入的额外延迟是导致第二个示例性能下降的主要原因。

+0

这就是我所怀疑的。我想也许executemany函数足够复杂,可以在一个查询中发送所有命令,但它看起来并不像它。 – 2010-10-15 20:15:52

1

强烈建议不要使用executeManypyodbc以及ceodbc都很慢并且包含很多错误。

请考虑使用execute并使用简单的字符串格式手动构造SQL查询。

transaction = "TRANSACTION BEGIN {0} COMMIT TRANSACTION 

bulkRequest = "" 
for i in range(0, 100) 
    bulkRequest = bulkRequest + "INSERT INTO ...... {0} {1} {2}" 

ceodbc.execute(transaction.format(bulkRequest)) 

当前实现非常简单快速可靠。

相关问题