Подтвердить что ты не робот

Почему slowmany замедляется в Python MySQLdb?

Я разрабатываю программу на Python, которая обращается к базе данных MySQL, используя MySQLdb. В некоторых ситуациях мне нужно запустить команду INSERT или REPLACE для многих строк. В настоящее время я делаю это так:

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, и похоже, что он ничего не должен делать.

4b9b3361

Ответ 1

Попробуйте уменьшить слово "значения" в запросе - это, по-видимому, ошибка/регрессия в MySQL-python 1.2.3.

Выполнение MySQL-python executeemany() соответствует предложению VALUES с регулярным выражением, а затем просто клонирует список значений для каждой строки данных, поэтому вы в конечном итоге выполняете точно такой же запрос, как и ваш первый подход.

К сожалению, регулярное выражение потеряло флагом без учета регистра в этой версии (впоследствии исправлено в trunk r622, но никогда не было обращено в ветвь 1.2), поэтому он ухудшает итерирование данных и отключение запроса в строке.

Ответ 2

Ваш первый пример - это один (большой) оператор, который сгенерирован и затем отправляется в базу данных.

Второй пример - гораздо более простой оператор, который вставляет/заменяет одну строку, но выполняется несколько раз. Каждая команда отправляется в базу данных отдельно, поэтому вы должны оплачивать время обработки с клиента на сервер и обратно для каждой вставленной строки. Я думаю, что эта дополнительная латентность, введенная между командами, является основной причиной снижения производительности второго примера.

Ответ 3

Не рекомендуется использовать executeMany в pyodbc, а также 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))

Текущая реализация очень простая и надежная.