Могут ли две инструкции SELECT FOR UPDATE в одной и той же таблице вызвать взаимоблокировку?

Предположим, что две одновременные транзакции выполняют следующие запросы к БД Postgresql:

Транзакция A:

SELECT * FROM mytable WHERE id IN (1, 2, 3, 4) FOR UPDATE

Транзакция Б:

SELECT * FROM mytable WHERE id IN (6, 3, 2, 1) FOR UPDATE

Возможно ли возникновение взаимоблокировки из-за того, что Postgresql получает блокировки строк в непоследовательном порядке? Например. если Postgresql будет получать блокировки строк в том порядке, в котором идентификаторы указаны в этом примере, то существует вероятность взаимоблокировки.

Или Postgresql внутренне достаточно умен, чтобы всегда получать блокировки строк таким образом, что одновременные дискретные операторы SELECT FOR UPDATE в одной и той же таблице не могут блокировать друг друга (например, всегда получая блокировки строк в порядке первичного ключа)?

Если Postgresql не автоматически предотвращает возникновение таких взаимоблокировок, есть ли способ изменить запросы, чтобы предотвратить такую ​​ситуацию (например, если на самом деле Postgresql получает блокировки строк в том порядке, в котором указаны идентификаторы, тогда последовательная сортировка идентификаторов должна предотвращать взаимоблокировку)?

Спасибо за любую помощь!


person Steve A    schedule 10.10.2012    source источник
comment
Этот вопрос подходит для dba.SE.   -  person Erwin Brandstetter    schedule 10.10.2012
comment
Набор IN( .. , ..) не подразумевает упорядоченный набор. Это просто набор (набор элементов), аналогичный результату select ..., который представляет собой (неупорядоченный) набор кортежей. Другими словами: порядок оценки/выполнения не определен   -  person wildplasser    schedule 11.10.2012
comment
На самом деле это не ответ на ваш вопрос, но, если вы боитесь взаимоблокировок, почему бы не повысить уровень изоляции SET TRANSACTION ISOLATION LEVEL SERIALIZABLE; Таким образом, второй SELECT будет отклонен.   -  person bortzmeyer    schedule 11.10.2012


Ответы (2)


Извините, у меня был другой ответ, но он был неправильным.

В документации указано, что предложение ORDER BY применяется перед предложением FOR UPDATE. Таким образом, блокировки приобретаются в любом порядке, в котором выбираются строки (я подтвердил это тестированием). Если вам нужно выбрать их в другом порядке, вы можете использовать:

SELECT * FROM (SELECT * FROM table ORDER BY id FOR UPDATE) ORDER BY another_column;

Вы можете задать свой вопрос в списке рассылки PostgreSQL.

person Dondi Michael Stroma    schedule 10.10.2012
comment
У вас есть ссылка на соответствующую страницу документа? То, что вы говорите, имеет смысл, но я не видел упоминания ORDER BY в postgresql.org/docs/9.1/static/explicit-locking.html. - person j_random_hacker; 11.10.2012
comment
Он находится в разделе SELECT/FOR UPDATE: postgresql.org/docs/9.1/static/ - person Dondi Michael Stroma; 11.10.2012
comment
Спасибо за ответ. Я видел это в документах, но мне не ясно, что фактическая блокировка строк выполняется для строк в порядке, определяемом предложением ORDER BY. Это описание может быть прочитано как говорящее, что ORDER BY просто предопределяет порядок вывода (который может перестать быть точным после получения всех блокировок), но не влияет на фактический порядок блокировок. - person Steve A; 11.10.2012

Из http://www.postgresql.org/docs/9.1/static/explicit-locking.html:

PostgreSQL автоматически обнаруживает тупиковые ситуации и разрешает их, прерывая одну из задействованных транзакций.

На этой странице используется пример с использованием UPDATEs, которые эквивалентны SELECT ... FOR UPDATE в отношении блокировки.

person j_random_hacker    schedule 10.10.2012
comment
Postgres может разрешать взаимоблокировки, да. Но это не дает ответа на вопрос, может ли вообще возникнуть взаимоблокировка в этой настройке. - person Erwin Brandstetter; 10.10.2012
comment
@ErwinBrandstetter: я ожидаю, что может, поскольку порядок строк, возвращаемых SELECT, не указан. Тем не менее, пункт ORDER BY должен это исправить. - person j_random_hacker; 10.10.2012