咨询锁或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 UPDATE
的NOWAIT
选项。 我不确定我理解使用pg_try_advisory_xact_lock()
和NOWAIT
选项之间的区别,他们在我看来实现了相同的目标:
-
NOWAIT
的目标 :要防止操作等待其他事务提交,请使用
NOWAIT
选项。 -
pg_try_advisory_xact_lock
的目标
不要等待上一个事务释放锁,仍然能够执行另一个事务,并且只操作下一个选择以更新“尚未锁定”的行。
哪一个更适合我的需要?
只是一个好主意,如果你坚持锁定一个特定的行,这不是你需要的。 您只需要任何合格的,可用的(未锁定的)行。 重要的区别在于( 引用Postgres 9.4的手册 ): FOR UPDATE NOWAIT
使用
NOWAIT
,如果无法立即锁定所选行,则语句将报告错误,而不是等待。
相同的查询很可能会尝试锁定相同的任意选择。 FOR UPDATE NOWAIT
将只是一个例外(除非你捕获错误,它将回滚整个事务)并且你必须重试。
我在dba.SE上引用的答案中的解决方案使用了普通FOR UPDATE
和pg_try_advisory_lock()
组合:
pg_try_advisory_lock
类似于pg_advisory_lock
,但该函数不会等待锁变为可用。 它将立即获得锁定并返回true,如果无法立即获取锁定,则返回false。
所以你最好的选择是……第三种选择: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
如引用的答案中所示:
- Postgres UPDATE … LIMIT 1
(还有FOR UPDATE SKIP LOCKED
。)
在旁边
严格来说,你是随意的,而不是真正的随机选择。 这可能是一个重要的区别。
您的查询的审核版本是我对您的其他问题的回答 。