2012-01-26 188 views
5

有時postgresql引發錯誤死鎖。postgresql死鎖

觸發器設置爲FOR UPDATE。

表評論:

http://pastebin.com/L1a8dbn4

日誌(INSERT語句中板缺):

2012-01-26 17:21:06 MSK ERROR: deadlock detected 
2012-01-26 17:21:06 MSK DETAIL: Process 2754 waits for ExclusiveLock on tuple (40224,15) of relation 735493 of database 734745; blocked by process 2053. 
Process 2053 waits for ShareLock on transaction 25162240; blocked by process 2754. 
Process 2754: INSERT INTO comment (user_id, content_id, reply_id, text) VALUES (1756235868, 935967, 11378142, 'text1') RETURNING comment.id; 
Process 2053: INSERT INTO comment (user_id, content_id, reply_id, text) VALUES (4071267066, 935967, 11372945, 'text2') RETURNING comment.id; 
2012-01-26 17:21:06 MSK HINT: See server log for query details. 
2012-01-26 17:21:06 MSK CONTEXT: SQL statement "SELECT comments_count FROM content WHERE content.id = NEW.content_id FOR UPDATE" 
PL/pgSQL function "increase_comment_counter" line 5 at SQL statement 
2012-01-26 17:21:06 MSK STATEMENT: INSERT INTO comment (user_id, content_id, reply_id, text) VALUES (1756235868, 935967, 11378142, 'text1') RETURNING comment.id; 

,並觸發對錶註釋:

CREATE OR REPLACE FUNCTION increase_comment_counter() RETURNS TRIGGER AS $$ 
DECLARE 
comments_count_var INTEGER; 
BEGIN 
    SELECT INTO comments_count_var comments_count FROM content WHERE content.id = NEW.content_id FOR UPDATE; 
    UPDATE content SET comments_count = comments_count_var + 1, last_comment_dt = now() WHERE content.id = NEW.content_id; 
    RETURN NEW; 
END; 
$$ LANGUAGE plpgsql; 



CREATE TRIGGER increase_comment_counter_trigger AFTER INSERT ON comment FOR EACH ROW EXECUTE PROCEDURE increase_comment_counter(); 

它爲什麼會發生?

謝謝!

回答

10

這兩個註釋是用相同的content_id插入的。僅僅插入註釋將取消內容行上的SHARE鎖定,以阻止另一個事務刪除該行,直到第一個事務完成。

但是,觸發器繼續將鎖升級爲EXCLUSIVE,並且可以通過執行相同過程的併發事務來阻止。考慮以下事件序列:

Txn 2754      Txn 2053 
Insert Comment 
           Insert Comment 
Lock Content#935967 SHARE 
    (performed by fkey) 
           Lock Content#935967 SHARE 
           (performed by fkey) 
Trigger 
Lock Content#935967 EXCLUSIVE 
(blocks on 2053's share lock) 
           Trigger 
           Lock Content#935967 EXCLUSIVE 
           (blocks on 2754's share lock) 

So-死鎖。

一種解決方案是立即之前採取對內容行獨佔鎖插入註釋。即

SELECT 1 FROM content WHERE content.id = 935967 FOR UPDATE 
INSERT INTO comment(.....) 

另一種解決方案是簡單地完全避免這種「緩存數」的格局,除非你能證明它是必要的性能。如果是這樣,考慮將緩存計數保持在除內容表之外的其他地方 - 例如櫃檯專用桌子。每次添加評論時,這也會減少內容表的更新流量。或者,也許只需重新選擇計數並在應用程序中使用memcached即可。不管你存儲這個緩存計數的哪個位置將成爲一個瓶頸,它都必須被安全地更新。

+0

謝謝!做得好! :) – lestat

+0

我寫了一些蟒蛇測試檢測死鎖,選擇更新似乎沒有幫助:( – lestat

+0

你正在做'select for update'並在同一事務中插入註釋?它實際上是阻止其他進程嘗試'選擇更新'還是他們都要通過插入? – araqnid