Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/ruby-on-rails/61.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Sql 建议锁定或NOWAIT以避免等待锁定的行?_Sql_Ruby On Rails_Postgresql_Ruby On Rails 4_Concurrency - Fatal编程技术网

Sql 建议锁定或NOWAIT以避免等待锁定的行?

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\"

在我的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"
) 
非常受启发

我只希望我的查询找到并更新第一行(随机,使用
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的链接,对未来的工作非常有意思。