咨询锁或NOWAIT以避免等待锁定的行?

在我的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 ,我需要在执行此操作时锁定该行,但不要让新请求等待发布之前的锁定,因为有许多并发调用将使用此查询。

但是我也看到了FOR UPDATENOWAIT选项。 我不确定我理解使用pg_try_advisory_xact_lock()NOWAIT选项之间的区别,他们在我看来实现了相同的目标:

  • NOWAIT的目标 :

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

  • pg_try_advisory_xact_lock的目标
    不要等待上一个事务释放锁,仍然能够执行另一个事务,并且只操作下一个选择以更新“尚未锁定”的行。

哪一个更适合我的需要?

FOR UPDATE NOWAIT只是一个好主意,如果你坚持锁定一个特定的行,这不是你需要的。 您只需要任何合格的,可用的(未锁定的)行。 重要的区别在于( 引用Postgres 9.4的手册 ):

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

相同的查询很可能会尝试锁定相同的任意选择。 FOR UPDATE NOWAIT将只是一个例外(除非你捕获错误,它将回滚整个事务)并且你必须重试。

我在dba.SE上引用的答案中的解决方案使用了普通FOR UPDATEpg_try_advisory_lock()组合:

pg_try_advisory_lock类似于pg_advisory_lock ,但该函数不会等待锁变为可用。 它将立即获得锁定并返回true,如果无法立即获取锁定,则返回false。

所以你最好的选择是……第三种选择:Postgres 9.5中新的FOR UPDATE SKIP LOCKED ,它实现了相同的行为,无需额外的函数调用。

Postgres 9.5的手册比较了两个选项,更多地解释了它们之间的区别:

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

在Postgres 9.4或更早版本中,您的下一个最佳选择是将pg_try_advisory_xact_lock(id)FOR UPDATE如引用的答案中所示:

  • Postgres UPDATE … LIMIT 1

(还有FOR UPDATE SKIP LOCKED 。)

在旁边

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