在我的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,而我需要在执行此操作时锁定该行,但无需发出新的请求来等待释放先前的锁,因为许多将使用此查询的 并发调用 。
LIMIT
available = true
available = false
但我也看到了NOWAIT选择FOR UPDATE。我不确定我是否理解使用pg_try_advisory_xact_lock()和NOWAIT选项之间的区别,在我看来它们似乎可以实现相同的目标:
NOWAIT
FOR UPDATE
pg_try_advisory_xact_lock()
为防止该操作等待其他事务提交,请使用该NOWAIT选项。
pg_try_advisory_xact_lock
哪一个更适合我的需求?
FOR UPDATE NOWAIT 如果您坚持锁定特定的行,这 不是 一个好主意,那 不是 您所需要的。您只需要 任何 符合条件的可用(解锁)行。重要的区别是此(引用Postgres 9.4手册):
FOR UPDATE NOWAIT
使用NOWAIT,如果无法立即锁定选定的行,该语句将报告错误,而不是等待。
相同的查询很可能会尝试锁定相同的任意选择。FOR UPDATE NOWAIT只会因出现异常而纾困(除非捕获错误,否则它将回滚整个事务),您必须重试。
我在dba.SE上引用的答案中的解决方案结合使用了plain FOR UPDATE 和以下内容 pg_try_advisory_lock() :
pg_try_advisory_lock()
pg_try_advisory_lock与相似pg_advisory_lock,不同之处在于该功能将不等待锁变为可用。它将立即获取锁并返回true,或者如果无法立即获取锁,则返回false。
pg_try_advisory_lock
pg_advisory_lock
因此, 最好的选择 是……第三种选择: FOR UPDATE SKIP LOCKED Postgres 9.5中的新功能,它实现了相同的行为,而没有额外的函数调用。
FOR UPDATE SKIP LOCKED
Postgres 9.5的手册比较了这两个选项,并进一步说明了不同之处:
为防止操作等待其他事务提交,请使用NOWAIT或SKIP LOCKED选项。使用NOWAIT,如果无法立即锁定选定的行,该语句将报告错误,而不是等待。使用SKIP LOCKED,将跳过所有不能立即锁定的选定行。
SKIP LOCKED
在Postgres 9.4或更早的版本上,您的 下一个最佳选择 是pg_try_advisory_xact_lock(id)与FOR UPDATE参考答案中所示的类似物组合使用:
pg_try_advisory_xact_lock(id)
(还带有的实现FOR UPDATE SKIP LOCKED。)
在旁边
严格来说,您会得到任意选择,而不是真正的随机选择。