postgresql死锁

时间:2012-01-26 16:15:29

标签: database postgresql triggers deadlock

有时postgresql会引发错误死锁。

在设置FOR 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();

为什么会发生?

谢谢!

1 个答案:

答案 0 :(得分: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)

死锁。

一种解决方案是立即在插入评论之前对内容行进行独占锁定。即。

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

另一种解决方案是完全避免这种“缓存计数”模式,除非您可以证明它对性能是必要的。如果是这样,请考虑将缓存计数保留在内容表以外的其他位置 - 例如一个专门的柜台。每次添加评论时,这也将减少内容表的更新流量。或者只是重新选择计数并在应用程序中使用memcached。无论如何存储这个缓存计数将成为一个阻塞点,它必须得到安全更新。