SELECT ... FOR UPDATE SKIP LOCKED 在 REPETABLE READ 事务中

Ynv*_*Ynv 5 sql postgresql transactions isolation-level locks

我的 PostgreSQL 10.5 数据库中有以下语句,我在repeatable read事务中执行:

delete from task
  where task.task_id = (
    select task.task_id
    from task
    order by task.created_at asc
    limit 1
    for update skip locked
  )
  returning
    task.task_id,
    task.created_at
Run Code Online (Sandbox Code Playgroud)

不幸的是,当我运行它时,我有时会得到:

[67] ERROR:  could not serialize access due to concurrent update
[67] STATEMENT:  delete from task
  where task.task_id = (
    select task.task_id
    from task
    order by task.created_at asc
    limit $1
    for update skip locked
  )
  returning
    task.task_id,
    task.created_at
Run Code Online (Sandbox Code Playgroud)

这意味着事务回滚,因为在此期间其他事务修改了记录。(我认为?)

我不太明白这一点。不同的事务如何修改使用 选择for update skip locked并删除的记录?

Erw*_*ter 10

手册中的这句话正好讨论了您的情况:

UPDATEDELETESELECT FOR UPDATESELECT FOR SHARE命令的行为与SELECT搜索目标行的行为相同:它们只会查找在事务开始时间提交的目标行。但是,这样的目标行在找到时可能已经被另一个并发事务更新(或删除或锁定)。在这种情况下,可重复读事务将等待第一个更新事务提交或回滚(如果它仍在进行中)。如果第一个更新器回滚,那么它的影响就被否定了,可重复读取事务可以继续更新最初找到的行。但是如果第一个更新程序提交(并且实际上更新或删除了该行,而不仅仅是锁定它)那么可重复读取事务将与消息一起回滚

ERROR:  could not serialize access due to concurrent update
Run Code Online (Sandbox Code Playgroud)

意思是,您的事务无法锁定行开始 - 由于首先到达那里的并发写访问。SKIP LOCKED无法完全避免这种情况,因为可能不再有锁可以跳过,如果行已经更改(并且更改已提交 - 因此锁已释放)自事务开始以来,我们仍然会遇到序列化失败。

相同的语句在默认READ COMMITTED事务隔离下应该可以正常工作。有关的: