Предположим, что some_table
имеет две строки с первичным ключом 1
и 2
. Следующая последовательность операторов может вызвать тупик:
session 1: begin;
session 2: begin;
session 1: DELETE FROM my_table WHERE my_key = 1;
session 2: DELETE FROM my_table WHERE my_key = 2;
session 1: DELETE FROM my_table WHERE my_key = 2;
session 2: DELETE FROM my_table WHERE my_key = 1;
Взаимоблокировки не произошло бы, если бы оба сеанса были удалены в одном и том же порядке.
Теперь, переходя к моему вопросу, что произойдет, если оператор DELETE коснется нескольких строк? Например:
session 1: begin;
session 2: begin;
session 1: DELETE FROM my_table;
session 2: DELETE FROM my_table;
Возможно ли, что два параллельных, но идентичных оператора DELETE удалят строки в другом порядке? Можно ли принудительно применить порядок удаления, чтобы избежать тупиковой ситуации?
Мне не удалось найти эту информацию в документации, поэтому я бы сказал, что порядок удаления не гарантируется (хотя это может быть косвенно как деталь реализации). Я хотел дважды проверить здесь.
@Ben AFAIK возможная взаимоблокировка может возникнуть во время оператора DELETE
(то есть как можно раньше). Его не следует откладывать до заявления COMMIT
, которого здесь действительно нет. Мой вопрос не о тривиальном тупике из-за отсутствия оператора COMMIT
.
«Транзакция» действительно вызывает «сериализацию» доступа. Это гарантирует, что вся обработка завершится успешно или не удастся. Он также заблокирует ожидание использования ресурса внутри ваших транзакций, которые обращаются к тем же ресурсам. Первое, что вы делаете, это получаете и блокируете ресурсы, необходимые для завершения текущего шага транзакции. В вашем случае заблокируйте все нужные строки (выберите для обновления) или заблокируйте таблицу, получите доступ к строкам и отпустите ее. Серьезно, самый простой способ - получить доступ к ресурсам в одном и том же порядке в каждой транзакции, чтобы предотвратить «взаимоблокировки».
@RyanVincent Это вообще мой вопрос. SELECT FOR UPDATE
, предшествующий DELETE
, не нужен, если строки всегда удаляются в одном и том же порядке, или там, если есть способ принудительно установить порядок удаления. Если нет, то SELECT ... ORDER BY ... FOR UPDATE (SKIP LOCKED)
действительно может быть полезен.
Что касается FOR UPDATE
, я не уверен, работает ли это так, как мы ожидали. См. stackoverflow.com/questions/51972202/…
Да, это может привести к тупиковой ситуации, потому что порядок строк в таблице не фиксирован.
Любой UPDATE
может изменить порядок строк, возвращаемых последовательным сканированием таблицы, и если synchronize_seqscans
имеет значение по умолчанию on
, порядок может измениться, даже если таблица не изменится, если несколько последовательных сканирований выполняются одновременно (как в вашем случае).
Вы должны сначала запустить SELECT ... FOR UPDATE
с предложением ORDER BY
, чтобы снизить риск тупиковой ситуации, но даже в этом случае вы не можете быть абсолютно уверены, если не выполните сортировку по столбцу, который не будет обновляться одновременно (например, первичный ключ).
Спасибо, это то, что я ожидал. Что касается добавления ORDER BY
, вы имели в виду что-то вроде этого: DELETE FROM my_table WHERE my_key IN (SELECT my_key FROM my_table ORDER MY my_key)
? DELETE
соблюдает порядок подзапроса?
Я недостаточно хорошо думал; Я отредактировал ответ. Предыдущий SELECT ... FOR UPDATE
должен помочь.
Большой! Это тоже была моя мысль, поэтому я создал stackoverflow.com/questions/51972202/….
Резюме: DELETE FROM my_table WHERE my_key IN (SELECT my_key FROM my_table ORDER BY my_key FOR UPDATE (SKIP LOCKED/NOWAIT)
должен давать детерминированный порядок удаления. При необходимости можно добавить SKIP LOCKED
или NOWAIT
.
Это должно работать, за исключением того, что NOWAIT
вызовет ошибку.
Это вызовет тупик, потому что вы не совершаете ни одной транзакции.