在我的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
选项。
不等待前一个事务释放锁定,并且仍然能够进行另一个事务,并且仅操作下一个选择以更新“尚未锁定”的行。
哪一个更适合我的需要?
1条答案
按热度按时间k2arahey1#
*
FOR UPDATE NOWAIT
只有在您坚持锁定特定行时才是一个好主意,这不是您需要的。您只需要 * 任何 * 符合条件的可用(未锁定)行。重要的区别是,(引用手册:)对于
NOWAIT
,如果不能立即锁定选定的行,则该语句将报告错误,而不是等待。相同的查询很可能会尝试锁定相同的任意pick。
FOR UPDATE NOWAIT
将通过一个异常退出(除非捕获错误,否则将回滚整个事务),您必须重试。我在dba.SE上引用的答案中的解决方案使用了普通**
FOR UPDATE
和pg_try_advisory_lock()
的组合:pg_try_advisory_lock
类似于pg_advisory_lock
,不同之处在于函数不会等待锁变得可用。它会立即获取锁并返回true,或者如果无法立即获取锁则返回false。所以你最好的选择是...第三种选择
FOR UPDATE SKIP LOCKED
(Postgres 9.5添加),实现相同的行为,无需额外的函数调用。手册解释了差异:
若要防止操作等待其他事务提交,请使用
NOWAIT
或SKIP 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。