Оптимизация одновременных обновлений в Postgres

9

Я выполняю параллельные запросы Postgres, например:

UPDATE foo SET bar = bar + 1 WHERE baz = 1234

Каждый запрос влияет на фиксированное число строк K, и я не могу найти способ обеспечить порядок, в котором строки обновляются, я получаю взаимоблокировки. В настоящее время я исправляю проблему, применяя порядок вручную, но это означает, что мне нужно выполнить гораздо больше запросов, чем обычно, одновременно увеличивая сложность поиска с O (log N + K) до O (K log N).

Есть ли способ улучшить производительность, не оказываясь уязвимым для тупиков? Я подозреваю, что замена (baz)индекса на (baz, id)индекс может сработать при условии, что Postgres обновляет строки в том же порядке, в котором они их сканировали. Стоит ли придерживаться такого подхода?

Алексей Аверченко
источник
Я предлагаю вам добавить CREATE TABLEкод.
ypercubeᵀᴹ

Ответы:

15

Нет ORDER BYв SQL UPDATEкоманде. Postgres обновляет строки в произвольном порядке:

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

Ваш лучший способ действий, вероятно, заключается в том, чтобы явно заблокировать его SELECT ... ORDER BY ... FOR UPDATEв подзапросе или в отдельной SELECTтранзакции - по умолчанию на уровне изоляции «чтение зафиксировано». Цитирую Тома Лейна на pgsql-general :

Все должно быть в порядке - блокировка FOR UPDATE всегда является последним шагом в конвейере SELECT.

Это должно сделать работу:

BEGIN;

SELECT 1
FROM   foo 
WHERE  baz = 1234
ORDER  BY bar
FOR    UPDATE;

UPDATE foo
SET    bar = bar + 1
WHERE  baz = 1234;

COMMIT;

Многостолбцовый индекс (baz, bar)может быть идеальным для производительности. Но так barкак он, очевидно, обновлен очень сильно , индекс в одну колонку (baz)может быть даже лучше. Зависит от пары факторов. Сколько строк в baz? Возможны ли горячие обновления без многоколоночного индекса? ...

Если baz он обновляется одновременно, маловероятно, что в случае конфликта возникнет угроза (согласно документации) :

Для SELECTкоманды, работающей на READ COMMITTED уровне изоляции транзакции, можно использовать ORDER BYпредложение блокировки и вернуть строки не по порядку. ...

Кроме того , если вы должны иметь уникальное ограничение с участием bar, рассмотрим DEFERRABLEограничение , чтобы избежать уникальных нарушений в одной и той же команды. Связанный ответ:

Эрвин Брандштеттер
источник
1
Если я заказываю по idили по какой-то другой уникальной колонке bar, не должно быть углового случая или падения производительности, верно?
Алексей Аверченко
@AlexeiAverchenko: Да, уникальный столбец, который никогда не обновляется, был бы идеальным для этого - и многоколонный индекс, включающий этот столбец на второй позиции.
Эрвин Брандштеттер