我注意到并发执行类似的简单且相同的查询
BEGIN;
SELECT files.data FROM files WHERE files.file_id = 123 LIMIT 1 FOR UPDATE;
UPDATE files SET ... WHERE files.file_id = 123;
COMMIT;
导致死锁,这使我感到惊讶,因为看起来这样的查询不应创建死锁。另外:通常只需几毫秒即可完成此请求。在这种死锁情况下,如果我运行:
SELECT blockeda.pid AS blocked_pid, blockeda.query as blocked_query,
blockinga.pid AS blocking_pid, blockinga.query as blocking_query FROM pg_catalog.pg_locks blockedl
JOIN pg_stat_activity blockeda ON blockedl.pid = blockeda.pid
JOIN pg_catalog.pg_locks blockingl ON(blockingl.transactionid=blockedl.transactionid
AND blockedl.pid != blockingl.pid)
JOIN pg_stat_activity blockinga ON blockingl.pid = blockinga.pid
WHERE NOT blockedl.granted;
在整个死锁期间,我看到为blocked_pid
和blockin_pid
列出的两个相同的select语句。
所以我的问题是:试图选择同一行FOR UPDATE
导致死锁的查询是否正常?如果是这样,在这种情况下避免死锁的最佳策略是什么?
答案 0 :(得分:2)
您的命令矛盾。
如果已将npm install react-scripts --save
(或files.file_id
)定义为UNIQUE
,则不需要PRIMARY KEY
。而且您根本不需要显式锁定。只需运行LIMIT 1
,由于在整个事务中只影响一行,因此不会出现死锁。 (除非触发器,规则或所涉及的函数有副作用)。
如果UPDATE
不是files.file_id
(看起来好像),那么UNIQUE
可以以任意顺序影响多行,并且只有其中一行被锁定,这是死锁的秘诀。那么,更直接的问题将是查询没有执行您似乎想开始的操作。
最佳解决方案取决于缺少的信息。这会起作用:
UPDATE
启用默认的自动提交功能后,单个命令不需要UPDATE files
SET ...
WHERE primary_key_column = (
SELECT primary_key_column
FROM files
WHERE file_id = 123
LIMIT 1
-- FOR UPDATE SKIP LOCKED
);
和BEGIN;
。
如果行已被锁定,您可能想添加FOR UPDATE SKIP LOCKED
(or FOR UPDATE NOWAIT
)来跳过或报告错误。
您可能想添加一个COMMIT;
子句,以避免重复处理同一行。
更多信息在这里: