“order by random() for update跳过锁定限制1”保证不锁定超过1行吗?

问题描述 投票:0回答:1

我确实在可序列化事务中对此进行了测试,它似乎按预期工作。但我想知道这里的保证。执行顺序是什么?我的期望是

order/skip->limit->lock

它会锁定多于一行的行吗?

我的实际场景: 我有一个表,其中每一行代表一个工作单元。我有多个并发工作人员,它们应该启动一个事务,通过

select ... where completed=false order by random() for update skip locked limit 1
保留一个工作单元,并在工作结束时更新包含
completed=true
列的行,然后再提交事务。如果预留返回零行或带有
SQL Error [40001]: ERROR: could not serialize access due to concurrent update
错误,则它将重新启动其事务。重点是避免在工作开始时立即失败来避免做不必要的工作。

我担心的是,即使只选择一行,是否也可能存在悬空锁?

咨询锁文档中,它对 LIMIT 查询发出警告:

因为在执行锁定函数之前不能保证 LIMIT 被应用。这可能会导致获取应用程序未预期的一些锁,因此无法释放(直到结束会话)。从应用程序的角度来看,这样的锁将是悬空的

但是这仅适用于咨询锁而不适用于 SELECT FOR UPDATE?我找不到这样的 SELECT FOR UPDATE 的警告文档。在 select docs 上只有一个关于无序返回行的警告,这在我的情况下不是问题,因为无论如何我只是随机排序。

postgresql multithreading transactions locking postgresql-16
1个回答
0
投票

是的,这是特定于咨询锁的,它是使用函数获取的。

要回答这样的问题,请查看执行计划。它看起来类似于这样:

EXPLAIN (COSTS OFF)
SELECT * FROM customers
ORDER BY random()
FOR NO KEY UPDATE SKIP LOCKED
LIMIT 1;
               QUERY PLAN                
═════════════════════════════════════════
 Limit
   ->  LockRows
         ->  Sort
               Sort Key: (random())
               ->  Seq Scan on customers
(5 rows)

排序后,行按照排序返回的顺序被锁定,一旦

LockRows
锁定第一行并将其传递到
Limit
节点,处理就会停止。

请注意,只有当您打算删除行或修改主键或唯一键时,

FOR UPDATE
才是正确的锁。对于正常的
UPDATE
,您应该使用
FOR NO KEY UPDATE
以避免过度锁定。

您似乎使用了高于已提交读的隔离级别,因为您收到了序列化错误。请注意,这并不要求该算法正确(但当然没问题)。

© www.soinside.com 2019 - 2024. All rights reserved.