【问题标题】:SELECT ... FOR UPDATE SKIP LOCKED in REPETABLE READ transactionsSELECT ... FOR UPDATE SKIP LOCKED 在 REPEATABLE READ 事务中
【发布时间】:2019-04-16 17:36:26
【问题描述】:

我的 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

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

[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

这意味着事务回滚,因为其他一些事务同时修改了记录。 (我认为?)

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

【问题讨论】:

    标签: sql postgresql transactions isolation-level locks


    【解决方案1】:

    This quote from the manual 讨论您的情况完全正确

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

    ERROR:  could not serialize access due to concurrent update
    

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

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

    【讨论】:

    • 感谢您的回复!我明白了,所以在可以获取锁之前,但在事务开始之后,另一个客户端设法删除了该行。我想要REPEATABLE READ 事务隔离的原因是因为在同一个事务中我将一行插入到第二个表中。使用READ COMMITED 这样做仍然安全吗?
    • @Ynv:是的,删除或更新并已提交的不同事务。 READ COMMITTED 仍然安全吗?可能是的,但这一切都取决于要求。对什么安全?我建议您开始一个新问题,并定义详细信息。 (如果 this 问题得到正确回答,请考虑接受它。)
    猜你喜欢
    • 2016-01-29
    • 1970-01-01
    • 2021-12-05
    • 2018-08-18
    • 2011-04-25
    • 1970-01-01
    • 2016-08-14
    • 1970-01-01
    • 2021-09-25
    相关资源
    最近更新 更多