postgresql 建议锁或NOWAIT以避免等待锁定的行?

iswrvxsc  于 2023-04-20  发布在  PostgreSQL
关注(0)|答案(1)|浏览(363)

在我的Rails 4应用程序中,我对Postgres9.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"
)

非常受this related answer on dba.SE的启发。
我只想让我的查询找到并更新available = true所在的第一行(随机,使用LIMIT),并将其更新为available = false,在执行此操作时,我需要锁定该行,但不需要发出新的请求,等待释放前一个锁,因为有许多并发调用将使用此查询。
但我也看到了NOWAIT选项到FOR UPDATE。我不确定我是否理解使用pg_try_advisory_xact_lock()NOWAIT选项之间的区别,在我看来,它们实现了相同的目标:

  • NOWAIT的目标:

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

不等待前一个事务释放锁定,并且仍然能够进行另一个事务,并且仅操作下一个选择以更新“尚未锁定”的行。
哪一个更适合我的需要?

k2arahey

k2arahey1#

*FOR UPDATE NOWAIT只有在您坚持锁定特定行时才是一个好主意,这不是您需要的。您只需要 * 任何 * 符合条件的可用(未锁定)行。重要的区别是,(引用手册:)

对于NOWAIT,如果不能立即锁定选定的行,则该语句将报告错误,而不是等待。
相同的查询很可能会尝试锁定相同的任意pick。FOR UPDATE NOWAIT将通过一个异常退出(除非捕获错误,否则将回滚整个事务),您必须重试。
我在dba.SE上引用的答案中的解决方案使用了普通**FOR UPDATEpg_try_advisory_lock()的组合:
pg_try_advisory_lock类似于pg_advisory_lock,不同之处在于函数不会等待锁变得可用。它会立即获取锁并返回true,或者如果无法立即获取锁则返回false。
所以你最好的选择是...第三种选择
FOR UPDATE SKIP LOCKED(Postgres 9.5添加),实现相同的行为,无需额外的函数调用。
手册解释了差异:
若要防止操作等待其他事务提交,请使用NOWAITSKIP LOCKED选项。对于NOWAIT,如果选定的行无法立即锁定,则该语句将报告错误,而不是等待。对于SKIP LOCKED,将跳过任何无法立即锁定的选定行。
对于Postgres 9.4或更早版本,您的
下一个最佳选择**是将pg_try_advisory_xact_lock(id)FOR UPDATE结合使用,如参考答案所示:

(Also用FOR UPDATE SKIP LOCKED实现)。

靠边

严格地说,你得到的是任意的,而不是真正的随机选择。这可能是一个重要的区别。
您的查询的审计版本是my answer to your other question

相关问题