Почему в MySQLdb Python выполняется медленно? - PullRequest
13 голосов
/ 15 октября 2010

Я разрабатываю программу на 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 и похоже, что он ничего не должен делать.

Ответы [ 4 ]

20 голосов
/ 16 октября 2010

Попробуйте опустить слово 'values' в вашем запросе - это похоже на ошибку / регрессию в MySQL-python 1.2.3.

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

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

1 голос
/ 31 октября 2014

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

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

1 голос
/ 16 октября 2010

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

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

0 голосов
/ 21 февраля 2019

Если вы используете mysqlclient-python (форк MySQLdb1), также рекомендуемый драйвер для Django (от Django), есть следующий пример использования, о котором вам нужно знать:

cursor.executemany возвращается к использованию cursor.execute (без вывода сообщений) в случае, если ваш запрос имеет вид:

INSERT INTO testdb.test (type, some_field, status, some_char_field) VALUES (%s, hex(%s), %s, md5(%s));

Драйвер использует регулярное выражение Python, которое, похоже, не поддерживает использование функций mysql в предложении VALUES.

    RE_INSERT_VALUES = re.compile(
    r"\s*((?:INSERT|REPLACE)\b.+\bVALUES?\s*)" +
    r"(\(\s*(?:%s|%\(.+\)s)\s*(?:,\s*(?:%s|%\(.+\)s)\s*)*\))" +
    r"(\s*(?:ON DUPLICATE.*)?);?\s*\Z",
    re.IGNORECASE | re.DOTALL)

Ссылка на соответствующий выпуск github https://github.com/PyMySQL/mysqlclient-python/issues/334

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...