2013-02-19 131 views
1

我在Postgres數據庫中看到了一些無法解釋的死鎖。簡化相關查詢,死鎖涉及的交易之一是:Postgres死鎖

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; 

我的問題是怎麼可能有一個僵局嗎?我無法想象第一個事務可能導致死鎖的任何場景,而不管同時運行任何其他查詢。

是否有這樣的情況下,即使用一個嵌套的SELECT ... FOR UPDATE可以是僵局的部分更新?

感謝

回答

0

(這是一個猜想,但希望一個受過教育的一個。)

一切取決於在這些行由選擇鎖定... ORDER BY O_ID的順序... FOR UPDATE。如果O_ID的順序是從ID的順序不同,那麼它是完全有可能具有類似於這樣的情況:

ID O_ID 
-- ---- 
1  2 
2  1 
  • 事務A鎖定與ID = 2的行。
  • 事務B鎖定ID = 1的行。
  • 事務A試圖鎖定與ID = 1的行中,但是被強制等待交易B.
  • 事務B試圖鎖定與ID = 2的行中,但是被強制等待交易A.

警告:即使O_ID順序與ID順序相同,ORDER BY子句實際上並不能保證鎖定的順序(它只是保證返回結果的順序)。不幸的是,這似乎很少記錄。對於它的價值,看起來Oracle在鎖定時並沒有(總是)遵守ORDER BY,所以我在PostgreSQL中也會小心。

一般來說,死鎖的解決方案總是以相同的順序鎖定。假設ORDER BY實際上保證了鎖定的順序,你可以在第二個事務中簡單地包含SELECT ... ORDER BY O_ID ... FOR UPDATE。或者,在第一筆交易中使用ORDER BY ID。

順便說一句,爲什麼你明確鎖定在第一位?你到底想要完成什麼?