2012-01-26 18 views
5

時々postgresqlがエラーデッドロックを発生させます。postgresqlデッドロック

テーブルが設定されたFOR UPDATEのトリガーです。

表のコメント:

http://pastebin.com/L1a8dbn4

ログ(INSERT文がcuttedさ):

テーブルコメント
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で挿入される2つのコメントです。コメントを挿入するだけで、最初のトランザクションが完了するまでその行を削除している別のトランザクションを停止するために、コンテンツ行の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) 

ソートデッドロック。

すぐにの排他ロックを取ってから、のコメントを挿入してください。すなわち

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

別の解決策は、あなたがそれがパフォーマンスのために必要であることを証明できる場合を除いて、完全にこの「キャッシュされた数」のパターンを避けることです。その場合は、キャッシュされたカウントをコンテンツ表以外のどこかに保存することを検討してください。カウンタ用の専用テーブル。これにより、コメントが追加されるたびにコンテンツテーブルへの更新トラフィックが削減されます。または、カウントを再選択して、アプリケーションでmemcachedを使用してください。このキャッシュされたカウントを格納する場所がチョークポイントになる場合は、安全に更新する必要があります。

+0

ありがとうございました!すごい仕事! :) – lestat

+0

私はデッドロックを検出するためにいくつかのpythonテストを書いていますが、アップデートのために選択すると助けにはならないようです:( – lestat

+0

あなたは ''選択更新 'をして、同じトランザクションにコメントを挿入していますか? 'select for update'か、どちらも挿入されているのですか? – araqnid