Оптимизация запросов к базе данных - PullRequest
2 голосов
/ 03 июня 2010

Хорошо, мои гигантские друзья, еще раз, я ищу немного места в ваших плечах: P

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

cursor.execute("UPDATE jiveuser SET username = '%s' WHERE userid = %d" % (newName,userId))

Это вызывается примерно 9500 раз с разными парами newName и userid ...

Есть предложения, как ускорить процесс? Может быть, каким-то образом можно сделать все обновления одним запросом?

Любая помощь будет высоко ценится!

PS: Postgres - это используемая дБ.

Ответы [ 8 ]

4 голосов
/ 03 июня 2010

Вставьте все данные в другую пустую таблицу (скажем, под названием userchanges), затем UPDATE в одном пакете:

UPDATE jiveuser
SET username = userchanges.username
FROM userchanges
WHERE userchanges.userid = jiveuser.userid
    AND userchanges.username <> jiveuser.username

См. Эту документацию по команде COPY для массовой загрузки ваших данных.

Есть также советов по повышению производительности при заполнении базы данных .

3 голосов
/ 04 июня 2010

Причина, по которой это занимает так много времени, заключается, вероятно, в том, что у вас включен автокоммит и каждое обновление выполняется в отдельной транзакции.

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

Решение состоит в том, чтобы сделать более одной строки на транзакцию. Но не делайте транзакции СЛИШКОМ большими или вы тоже столкнетесь с проблемами. Попробуйте зафиксировать каждые 10 000 строк изменений как приблизительное предположение.

3 голосов
/ 03 июня 2010

Прежде всего, не используйте оператор% для построения вашего SQL. Вместо этого передайте ваш кортеж аргументов в качестве второго параметра в cursor.execute, что также отменяет необходимость заключать ваш аргумент в кавычки и позволяет использовать% s для всего:

cursor.execute("UPDATE jiveuser SET username = %s WHERE userid = %s", (newName, userId))

Это важно для предотвращения атак SQL-инъекций .

Чтобы ответить на ваш вопрос, вы можете ускорить эти обновления, создав индекс для столбца userid, который позволит базе данных обновляться за O(1) постоянное время, а не сканировать всю таблицу базы данных, которая O(n). Поскольку вы используете PostgreSQL, вот синтаксис для создания индекса:

CREATE INDEX username_lookup ON jiveuser (userid);

РЕДАКТИРОВАТЬ: так как ваш комментарий показывает, что у вас уже есть индекс в столбце userid, вы не могли бы сделать что-либо, чтобы ускорить этот запрос. Таким образом, ваш основной выбор - либо жить с медлительностью, так как это звучит как единовременное исправление, либо следовать совету VeeArr и проверить, даст ли cursor.executemany достаточное ускорение.

2 голосов
/ 03 июня 2010

Возможно, вы захотите посмотреть executemany(): Информация здесь

1 голос
/ 04 июня 2010

Я бы объяснил это. Если он выполняет индексированный поиск, чтобы найти запись - что следует делать, если у вас есть индекс по идентификатору пользователя - тогда я не вижу, что вы могли бы сделать для повышения производительности. Если он не использует индекс, то нужно выяснить, почему нет и исправить его.

О, вы можете попробовать использовать подготовленное утверждение. С 9500 вставками это должно помочь.

1 голос
/ 03 июня 2010

Возможно, вы сможете создать индекс для идентификатора пользователя, чтобы ускорить процесс.

0 голосов
/ 07 июня 2010

Сначала убедитесь, что у вас есть индекс для 'userid', это гарантирует, что dbms не будет выполнять сканирование таблицы каждый раз

CREATE INDEX jiveuser_userid ON jiveuser (userid);

Затем попробуйте подготовить инструкцию, а затем вызвать executeЭто.Это остановит оптимизатор от необходимости проверять запрос каждый раз

PREPARE update_username(string,integer) AS UPDATE jiveuser SET username = $1 WHERE userid = $2;
EXECUTE update_username("New Name", 123);

Наконец, чуть более высокую производительность можно снизить, отключив autocommit

\set autocommit off
0 голосов
/ 03 июня 2010

Переместите это в хранимую процедуру и выполните ее из базы данных самостоятельно

...