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

Тупики в PostgreSQL при запуске UPDATE

Я немного запутался, прочитав о блокировках PostgreSQL.

Типичный пример взаимоблокировки:

-- Transaction 1
UPDATE customer SET ... WHERE id = 1
UPDATE customer SET ... WHERE id = 2

-- Transaction 2
UPDATE customer SET ... WHERE id = 2
UPDATE customer SET ... WHERE id = 1

Но что, если я изменю код следующим образом:

-- Transaction 1
UPDATE customer SET ... WHERE id IN (1, 2)

-- Transaction 2
UPDATE customer SET ... WHERE id IN (1, 2)

Будет ли здесь тупик?

По существу мой вопрос: во втором случае PostgreSQL блокирует строки один за другим или блокирует всю область, охватываемую условием WHERE?

Спасибо заранее!

4b9b3361

Ответ 1

В PostgreSQL строки будут заблокированы по мере их обновления - на самом деле способ, которым это фактически работает, состоит в том, что каждый кортеж (версия строки) имеет системное поле с именем xmin, чтобы указать, какая транзакция сделала этот кортеж текущей (путем вставки или обновления) и системного поля, называемого xmax, чтобы указать, какая транзакция истекла, что кортеж (путем обновления или удаления). Когда вы получаете доступ к данным, он проверяет каждый кортеж, чтобы определить, является ли он видимым для вашей транзакции, путем проверки вашего активного "моментального снимка" на эти значения.

Если вы выполняете UPDATE и кортеж, который соответствует вашим условиям поиска, имеет xmin, который сделает его видимым для моментального снимка и xmax активной транзакции, он блокирует, ожидая завершения этой транзакции. Если транзакция, которая сначала обновила кортеж, откат, ваша транзакция просыпается и обрабатывает строку; если первая транзакция завершается, транзакция просыпается и принимает действие в зависимости от текущего уровня изоляции транзакции.

Очевидно, что тупик является результатом этого события в разных строках. В ОЗУ нет блокировки на уровне строк, которые могут быть получены для всех строк одновременно, но если строки обновляются в том же порядке, вы не можете иметь циклическую блокировку. К сожалению, предложенный синтаксис IN(1, 2) не гарантирует этого. В разных сеансах могут быть разные факторы калькуляции, фоновая "аналитическая" задача может изменять статистику для таблицы между генерацией одного плана и другой, или может использоваться seqscan и подвержена влиянию оптимизации PostgreSQL, которая вызывает новый seqscan присоединиться к уже существующему и "обойти" для уменьшения ввода/вывода диска.

Если вы делаете обновления по одному в том же порядке, в коде приложения или с помощью курсора, тогда у вас будет только простая блокировка, а не взаимоблокировки. В целом, однако, реляционные базы данных подвержены ошибкам с сериализацией, и лучше всего получить к ним доступ через фреймворк, который распознает их на основе SQLSTATE и автоматически повторит всю транзакцию с самого начала. В PostgreSQL при сбое в сериализации всегда будет SQLSTATE из 40001 или 40P01.

http://www.postgresql.org/docs/current/interactive/mvcc-intro.html