Sql 建议锁定或NOWAIT以避免等待锁定的行?
在我的Rails 4应用程序中,我对Postgres9.4数据库进行了以下查询:Sql 建议锁定或NOWAIT以避免等待锁定的行?,sql,ruby-on-rails,postgresql,ruby-on-rails-4,concurrency,Sql,Ruby On Rails,Postgresql,Ruby On Rails 4,Concurrency,在我的Rails 4应用程序中,我对Postgres9.4数据库进行了以下查询: @chosen_opportunity = Opportunity.find_by_sql( " UPDATE \"opportunities\" s SET opportunity_available = false FROM ( SELECT \"opportunities\".* FROM \"opportunities\"
@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"
)
非常受启发
我只希望我的查询找到并更新第一行(随机,使用LIMIT
),其中available=true
,并将其更新为available=false
,我需要在执行此操作时锁定该行,但不需要发出新的请求,等待前一个锁的释放,因为有许多并发调用将使用此查询
但是我也看到了更新的NOWAIT
选项。我不确定我是否理解使用pg\u try\u advical\u xact\u lock()
和NOWAIT
选项之间的区别,在我看来,它们似乎达到了相同的目标:
- :
要防止操作等待其他事务提交,请使用
选项NOWAIT
不是等待上一个事务释放锁,仍然能够执行另一个事务,并且只操作下一个select更新“尚未锁定”行
FOR UPDATE NOWAIT
只有在您坚持锁定特定行时才是一个好主意,而这并不是您需要的。您只需要任何符合条件的可用(未锁定)行。重要的区别在于():
使用NOWAIT
,如果无法立即锁定所选行,则语句将报告错误,而不是等待
相同的查询很可能会尝试锁定相同的任意拾取FOR UPDATE NOWAIT
只会跳出一个异常(将回滚整个事务,除非您捕获错误),您必须重试
我在dba.SE上的参考答案中的解决方案使用普通的组合进行更新
,并结合:
pg\u try\u advisional\u lock
与pg\u advisional\u lock
类似,除了
功能不会等待锁可用。会的
立即获取锁并返回true,如果需要,则返回false
无法立即获取锁
所以你最好的选择是。。。第三种选择:Postgres 9.5中新的更新跳过锁定的,它实现了相同的行为而无需额外的函数调用
比较这两个选项,进一步解释差异:
防止操作等待其他事务处理完成
提交,使用NOWAIT
或SKIP LOCKED
选项。使用NOWAIT
语句报告错误,而不是等待所选行
无法立即锁定。当跳过锁定时
,任何选定的行
无法立即锁定,但已跳过
在Postgres 9.4或更高版本上,您的下一个最佳选择是使用pg\u try\u advical\u xact\u lock(id)
与一起进行更新
,如参考答案中所示:
(也可使用实现更新跳过锁定
)
在一边
严格地说,你得到的是任意选择,而不是真正的随机选择。这可能是一个重要的区别。
您的查询已审核。非常感谢您的详细回答。我将在下周一尝试实施。是的,你是对的,更新跳过锁定是最合适的。我可能会等待它的释放,同时使用pg顾问锁。因为我的行在那个阶段已经随机分布了,所以如果我没有得到任意选择,这并不重要。@Mathieu:“任意”也很重要,因为它意味着Postgres通常会为同一查询任意选择同一行,这使得锁争用问题比真正的随机选择问题要大得多。你说“它总是选择同一行”是什么意思?我的应用程序是一种彩票,如果你想的话。玩家同时进行呼叫,当玩家检查“ticket=opportunity”时,我将其更新为available=false。当玩家的下一个镜头出现时,它将使用我们正在记录的查询,并选择opportunity\u available=true的任何行。因此,如果查询总是选择同一行,因为它将在玩家打开后立即更新为不可用,我认为这一任意方面不会影响我。你…吗?postgresql新手here@Mathieu:如果查询未指定排序依据,Postgres可以选择最有效的方式以任何顺序返回符合条件的行。通常这是一个顺序扫描。因此,顺序不是随机的,而是任意的:同一个查询可能(但也不一定)以相同的顺序返回相同的行,即使没有orderby
。对于像你这样的排队系统来说,这是不幸的——但通常比强加随机顺序还要快。表中行的当前物理位置可以在中找到,并且可以随时更改。由于我已经在1000个机会中随机分配了10个“奖品”,因此我认为postgresql任意选择行对我来说并不重要。只要它“任意”选择符合select查询的行(可用的行=true),应用程序就会工作。无论如何,感谢ctid的链接,对未来的工作非常有意思。