2013-02-19 2 views
1

Я вижу некоторые необъяснимые тупики в нашей базе данных Postgres. Упрощая соответствующих запросов, один из сделок, участвующих в тупике является:Postgres deadlock

BEGIN; 
UPDATE A SET CHUNK_ID=1, STATUS='PROCESSING' WHERE ID IN (
    SELECT ID FROM A 
    WHERE CHUNK_ID IS NULL 
    ORDER BY O_ID 
    LIMIT 1000 
    FOR UPDATE 
); 
COMMIT; 

, а другой является:

BEGIN; 
UPDATE A SET STATUS='SENT' WHERE ID = 1; 
UPDATE A SET STATUS='SENT' WHERE ID = 2; 
UPDATE A SET STATUS='SENT' WHERE ID = 3; 
... 
COMMIT; 

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

Есть ли такой случай, то есть UPDATE с использованием вложенного SELECT ... FOR UPDATE может быть частью тупика?

Благодаря

ответ

0

(Это предположение, но, надеюсь, образованный один.)

Все зависит от того, в какие строки заблокированного SELECT ... ORDER BY O_ID ... ДЛЯ ОБНОВЛЕНИЯ. Если порядок O_ID отличается от порядка ID, то это вполне возможно иметь ситуацию, подобную этой:

ID O_ID 
-- ---- 
1  2 
2  1 
  • Транзакция А блокирует строку с ID = 2.
  • Сделка B блокирует строку с ID = 1.
  • сделка A пытается заблокировать строку с ID = 1, но вынужден ждать на транзакции B.
  • Транзакция B пытается заблокировать строку с ID = 2, но вынужден ждать на транзакции A.

ВНИМАНИЕ: Даже если заказ O_ID совпадает с порядком идентификатора, возможно, что предложение ORDER BY фактически не гарантирует порядок блокировки (он просто гарантирует порядок, в котором возвращаются результаты). К сожалению, это плохо документировано. Для чего это стоит, похоже, Oracle не (всегда) чтит ORDER BY, когда дело доходит до блокировки, поэтому я буду осторожен и в PostgreSQL.

Как правило, решение взаимоблокировок должно всегда блокироваться в том же порядке. Предполагая, что ORDER BY фактически гарантирует порядок блокировки, вы можете просто включить SELECT ... ORDER BY O_ID ... FOR UPDATE во второй транзакции. Или, наоборот, используйте ORDER BY ID в первой транзакции.

BTW, почему вы явно блокируете в первую очередь? Что именно вы пытаетесь сделать с этим?