Mat*_*ieu 4 sql postgresql concurrency ruby-on-rails ruby-on-rails-4
在我的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"
)
Run Code Online (Sandbox Code Playgroud)
非常受到dba.SE相关答案的启发.
我只是希望我的查询找到并更新第一个(随机,有LIMIT)行,available = true并将其更新到available = false,我需要在执行此操作时锁定行,但不要让新请求等待释放上一个锁,因为有许多将使用此查询的并发调用.
但我也看到了NOWAIT选项FOR UPDATE.我不确定我理解使用pg_try_advisory_xact_lock()和NOWAIT选项之间的区别,他们似乎在我看来实现了同样的目标:
要防止操作等待其他事务提交,请使用该
NOWAIT选项.
pg_try_advisory_xact_lock我们的目标
不是等待上一个事务释放锁定,仍然可以执行另一个事务,并且只运行下一个选择来更新"尚未锁定"的行.
哪一个更适合我的需要?
如果你坚持要锁定一个特定的行,这是一个好主意,这不是你需要的.您只需要任何合格的,可用的(未锁定的)行.重要的区别在于(引用Postgres 9.4的手册):FOR UPDATE NOWAIT
随着
NOWAIT,该语句报告错误,而不是等待,如果选择的行不能立即锁定.
相同的查询很可能会尝试锁定相同的任意选择.FOR UPDATE NOWAIT只会出现异常(除非你捕获错误,它将回滚整个事务)并且你必须重试.
我在dba.SE上引用的答案中的解决方案使用了plain FOR UPDATE 的组合和pg_try_advisory_lock():
pg_try_advisory_lock类似于pg_advisory_lock,除了函数不会等待锁变为可用.它将立即获得锁定并返回true,如果无法立即获取锁定,则返回false.
所以你最好的选择是......第三种选择:FOR UPDATE SKIP LOCKEDPostgres 9.5中的新选项,它实现相同的行为而无需额外的函数调用.
Postgres 9.5的手册比较了两个选项,更多地解释了这些差异:
要防止操作等待其他事务提交,请使用
NOWAIT或SKIP LOCKED选项.随着NOWAIT,该语句报告错误,而不是等待,如果选择的行不能立即锁定.使用时SKIP LOCKED,将跳过任何无法立即锁定的选定行.
在Postgres 9.4或更早版本中,您的下一个最佳选择是pg_try_advisory_xact_lock(id)与FOR UPDATE参考答案中所示的结合使用:
(还有一个实现 FOR UPDATE SKIP LOCKED.)
严格来说,你是随意的,而不是真正的随机选择.这可能是一个重要的区别.
您的查询的审核版本是我对您的其他问题的回答.