【发布时间】:2020-11-24 22:25:22
【问题描述】:
我有几个查询的交易。首先,选择带有FOR UPDATE 锁的行:
SELECT f.source_id FROM files AS f WHERE
f.component_id = $1 AND
f.archived_at IS NULL
FOR UPDATE
接下来是更新查询:
UPDATE files AS f SET archived_at = NOW()
WHERE
hw_component_id = $1 AND
f.source_id = ANY($2::text[])
然后是插入:
INSERT INTO files AS f (
source_id,
...
)
VALUES (..)
ON CONFLICT (component_id, source_id) DO UPDATE
SET archived_at = null,
is_valid = excluded.is_valid
我有两个应用程序实例,有时我会在 PostgreSQL 日志中看到死锁错误:
ERROR: deadlock detected
DETAIL: Process 3992939 waits for ShareLock on transaction 230221362; blocked by process 4108096.
Process 4108096 waits for ShareLock on transaction 230221365; blocked by process 3992939.
Process 3992939: SELECT f.source_id FROM files AS f WHERE f.component_id = $1 AND f.archived_at IS NULL FOR UPDATE
Process 4108096: INSERT INTO files AS f (source_id, ...) VALUES (..) ON CONFLICT (component_id, source_id) DO UPDATE SET archived_at = null, is_valid = excluded.is_valid
CONTEXT: while locking tuple (41116,185) in relation \"files\"
我认为这可能是由ON CONFLICT DO UPDATE 语句引起的,它可能会更新之前的SELECT FOR UPDATE 未锁定的行
但我不明白如果SELECT ... FOR UPDATE 查询是事务中的第一个查询,它怎么会导致死锁。在它之前没有查询。
SELECT ... FOR UPDATE 语句可以锁定几行,然后等待其他有条件的行被解锁吗?
【问题讨论】:
-
select...for update正是这样做的。如果您不希望它等待锁定的行,请在末尾添加skip locked。请参阅文档:postgresql.org/docs/current/… -
@MikeOrganek:
SKIP LOCKED似乎是错误的工具。你可能在想NOWAIT? -
@ErwinBrandstetter 一个甚至一个超时都可能是合适的。很难判断 OP 试图用不连贯的 sn-ps 代码来完成什么。
-
SKIP LOCKED仅适用于排队类型的查询,您继续使用实际锁定的行。似乎不适用。 -
@ErwinBrandstetter 在
select. . . for update运行之前,其他一些进程正在锁定行。在我看来,无论 OP 正在做什么,都应该重构为排队操作,但同样,我在这里没有看到足够的信息来说明情况。告诉我我的 cmets 错了,我会删除它们。
标签: sql postgresql concurrency deadlock select-for-update