PostgreSQL - FOR UPDATE SKIP LOCKED死锁
PostgreSQL - FOR UPDATE SKIP LOCKED deadlock
我有一个在 PostgreSQL 中使用队列 table 的并行进程。逻辑是:
- 开始交易。
- 用一些随机生成的 ID 标记 100 条未处理的记录。
- 提交。
- 运行 一些繁重的应用程序逻辑需要一些时间,并且正在处理具有在步骤 2 中生成的 ID 的队列记录。
- 更新 100 条状态为 success/bad 的已处理记录。
最多 20 个线程正在执行这些步骤。
但是,有时当我尝试使用查询执行 2 步时:
UPDATE QUEUE_TABLE
SET QUEUE_TXN_GUID=$RANDOM_GUID,
QUEUE_STATUS=1
WHERE QUEUE_ROW_GUID IN
(SELECT QUEUE_ROW_GUID from QUEUE_TABLE
WHERE QUEUE_STATUS IS NULL OR QUEUE_STATUS = -1
LIMIT 100 FOR UPDATE SKIP LOCKED) RETURNING QUEUE_ROW_GUID
我收到错误 deadlock detected
。
我在第 5 步中使用的查询是
UPDATE QUEUE_TABLE SET CDC_QUEUE_REZ_STATUS=$STATUS WHERE CDC_QUEUE_REZ_TXN_GUID=$RANDOM_GUID;
我不知道为什么我会遇到这个奇怪的死锁,第一个更新子查询中有 FOR UPDATE SKIP LOCKED
。
问题的原因是 QUEUE_ROW_GUID
中存在重复项。 Select 锁定了一些行,但随后查询更新的不是那些被锁定的行。这就是为什么并发 运行 查询可能会尝试更新与这一行相同的行。所以 SKIP LOCKED
在这种情况下不起作用。
鉴于行的更新可能以不同的顺序发生,第一个查询(尝试更新第 1 行和第 2 行)可能首先更新第 1 行,然后尝试更新第 2 行,但等待锁定。同时 运行 查询(也尝试更新 1 和 2)已经更新了第 2 行并等待第 1 行的锁定。因此出现死锁。
您需要在行被锁定后使用唯一标识符来更新它们。
我有一个在 PostgreSQL 中使用队列 table 的并行进程。逻辑是:
- 开始交易。
- 用一些随机生成的 ID 标记 100 条未处理的记录。
- 提交。
- 运行 一些繁重的应用程序逻辑需要一些时间,并且正在处理具有在步骤 2 中生成的 ID 的队列记录。
- 更新 100 条状态为 success/bad 的已处理记录。
最多 20 个线程正在执行这些步骤。 但是,有时当我尝试使用查询执行 2 步时:
UPDATE QUEUE_TABLE
SET QUEUE_TXN_GUID=$RANDOM_GUID,
QUEUE_STATUS=1
WHERE QUEUE_ROW_GUID IN
(SELECT QUEUE_ROW_GUID from QUEUE_TABLE
WHERE QUEUE_STATUS IS NULL OR QUEUE_STATUS = -1
LIMIT 100 FOR UPDATE SKIP LOCKED) RETURNING QUEUE_ROW_GUID
我收到错误 deadlock detected
。
我在第 5 步中使用的查询是
UPDATE QUEUE_TABLE SET CDC_QUEUE_REZ_STATUS=$STATUS WHERE CDC_QUEUE_REZ_TXN_GUID=$RANDOM_GUID;
我不知道为什么我会遇到这个奇怪的死锁,第一个更新子查询中有 FOR UPDATE SKIP LOCKED
。
问题的原因是 QUEUE_ROW_GUID
中存在重复项。 Select 锁定了一些行,但随后查询更新的不是那些被锁定的行。这就是为什么并发 运行 查询可能会尝试更新与这一行相同的行。所以 SKIP LOCKED
在这种情况下不起作用。
鉴于行的更新可能以不同的顺序发生,第一个查询(尝试更新第 1 行和第 2 行)可能首先更新第 1 行,然后尝试更新第 2 行,但等待锁定。同时 运行 查询(也尝试更新 1 和 2)已经更新了第 2 行并等待第 1 行的锁定。因此出现死锁。
您需要在行被锁定后使用唯一标识符来更新它们。