一尘不染

建议性锁定还是NOWAIT以避免等待锁定的行?

sql

在我的Rails 4应用程序中,我将查询查询到Postgres 9.4 数据库:

@chosen_opportunity = Opportunity.find_by_sql(
  " UPDATE \"opportunities\" s
    SET opportunity_available = false
    FROM (
          SELECT \"opportunities\".*
          FROM   \"opportunities\"
          WHERE  ( deal_id = #{@deal.id}
          AND    opportunity_available = true 
          AND    pg_try_advisory_xact_lock(id) )
          LIMIT  1
          FOR    UPDATE
          ) sub
    WHERE       s.id = sub.id
    RETURNING   sub.prize_id, sub.id"
)

dba.SE上的相关答案极大地启发了我们

我只希望查询在其中找到并更新第一LIMIT行(available = true并将其随机更新为),然后将其更新为available = false,而我需要在执行此操作时锁定该行,但无需发出新的请求来等待释放先前的锁,因为许多将使用此查询的 并发调用

但我也看到了NOWAIT选择FOR UPDATE。我不确定我是否理解使用pg_try_advisory_xact_lock()NOWAIT选项之间的区别,在我看来它们似乎可以实现相同的目标:

为防止该操作等待其他事务提交,请使用该NOWAIT选项。

  • pg_try_advisory_xact_lock的目标
    不是等待上一个事务释放锁定,而是仍然能够执行另一个事务,并且仅操作下一个select来更新“尚未锁定”的行。

哪一个更适合我的需求?


阅读 176

收藏
2021-03-17

共1个答案

一尘不染

FOR UPDATE NOWAIT 如果您坚持锁定特定的行,这 不是 一个好主意,那 不是
您所需要的。您只需要 任何 符合条件的可用(解锁)行。重要的区别是此(引用Postgres
9.4手册
):

使用NOWAIT,如果无法立即锁定选定的行,该语句将报告错误,而不是等待。

相同的查询很可能会尝试锁定相同的任意选择。FOR UPDATE NOWAIT只会因出现异常而纾困(除非捕获错误,否则它将回滚整个事务),您必须重试。

我在dba.SE上引用的答案中的解决方案结合使用了plain FOR UPDATE 和以下内容
pg_try_advisory_lock()

pg_try_advisory_lock与相似pg_advisory_lock,不同之处在于该功能将不等待锁变为可用。它将立即获取锁并返回true,或者如果无法立即获取锁,则返回false。

因此, 最好的选择 是……第三种选择: FOR UPDATE SKIP LOCKED Postgres
9.5中的新功能,它实现了相同的行为,而没有额外的函数调用。

Postgres 9.5的手册比较了这两个选项,并进一步说明了不同之处:

为防止操作等待其他事务提交,请使用NOWAITSKIP LOCKED选项。使用NOWAIT,如果无法立即锁定选定的行,该语句将报告错误,而不是等待。使用SKIP LOCKED,将跳过所有不能立即锁定的选定行。

在Postgres 9.4或更早的版本上,您的 下一个最佳选择pg_try_advisory_xact_lock(id)FOR UPDATE参考答案中所示的类似物组合使用:

(还带有的实现FOR UPDATE SKIP LOCKED。)

在旁边

严格来说,您会得到任意选择,而不是真正的随机选择。那可能是一个重要的区别。我对其他问题的回答
是您查询的审计版本。

2021-03-17