Sql 将pg_try_advision_xact_lock()放入嵌套子查询中?

Sql 将pg_try_advision_xact_lock()放入嵌套子查询中?,sql,ruby-on-rails,ruby-on-rails-3,postgresql,concurrency,Sql,Ruby On Rails,Ruby On Rails 3,Postgresql,Concurrency,在我的Ruby on Rails 4应用程序中,我对Postgres 9.4数据库进行了以下查询: @chosen_opportunity = Opportunity.find_by_sql( " UPDATE \"opportunities\" s SET opportunity_available = false FROM ( SELECT \"opportunities\".* FROM \"opportunities\"

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

但是这里()他们说,如果我没有弄错的话,我不应该在
WHERE
子句中使用
pg\u try\u advisional\u lock()
,因为我会在扫描的整个集合中每行调用一次(作为WHERE子句中发生的过滤的一部分)

我只希望我的查询找到并更新第一行(随机,使用
LIMIT
),其中
available=true
,并将其更新为
available=false
,我需要在执行此操作时锁定该行,但不需要发出新的请求,等待前一个锁的释放,因此我添加了建议锁


我是否应该将
pg\u try\u advisional\u lock()
放在
WHERE
子句之外?怎么做?

我更新了参考答案,添加了更多解释和链接。
在Postgres 9.5(目前为beta版)中,新的
跳过锁定
是一个优越的解决方案:


首先,让我简化查询中的几件事:

直截了当的询问
  • 所有的双引号只是你合法的小写名字的噪音
  • 由于opportunity\u available是一个布尔列,您可以将
    opportunity\u available=true
    简化为
    opportunity\u available
  • 您不需要从子查询返回
    *
    ,只要
    id
    就足够了
通常情况下,它按原样工作。解释如下

避免在不相关的行上使用通知锁 可以肯定的是,在下一个查询级别中应用
pg\u try\u advision\u xact\u lock()
之前,可以使用
OFFSET 0
hack(开销较小)将所有谓词封装在CTE或子查询中:

UPDATE opportunities s
SET    opportunity_available = false
FROM (
   SELECT id
   FROM  ( 
      SELECT id
      FROM   opportunities
      WHERE  deal_id = #{@deal.id}
      AND    opportunity_available
      AND    pg_try_advisory_xact_lock(id)
      OFFSET 0
      ) sub1
   WHERE  pg_try_advisory_xact_lock(id)
   LIMIT  1
   FOR    UPDATE
   ) sub2
WHERE     s.id = sub.id
RETURNING s.prize_id, s.id;
然而,这通常要贵得多

你可能不需要这个 如果您将查询建立在覆盖所有谓词的索引上,则不会有任何“附带”建议锁,如以下部分索引:

CREATE INDEX opportunities_deal_id ON opportunities (deal_id)
WHERE opportunity_available;
检查
EXPLAIN
以验证Postgres是否实际使用了索引。这样一来,
pg\u try\u advision\u xact\u lock(id)
将成为索引或位图索引扫描的过滤条件,并且开始时只测试(并锁定)符合条件的行,因此您可以使用简单表单而无需额外嵌套。同时,您的查询性能也得到了优化。我会那样做的

即使几行不相关的行偶尔会得到一个通知锁,这通常都无关紧要。咨询锁仅与实际使用咨询锁的查询相关。或者您真的有其他并发事务也使用建议锁并以同一表的其他行为目标吗?真的吗


唯一另一个有问题的情况是,如果大量不相关的行获得建议锁,这只能通过顺序扫描发生,即使在这种情况下也不太可能发生。

@patrick,没有。另一个问题()是关于相同的查询,但主题不同:我应该使用pg\u try\u advicial\u lock还是NOWAIT选项?同一代码上完全不同的问题。@patrick,或者可能纯粹是巧合,因为我是postgresql的新手,NOWAIT是目前这个问题的答案?我不知所措,苦苦思索这个问题,所以我打开了一个新问题:转到另一个问题:我认为问题来自fast,(你在这里的直觉中发现了这一点,呵呵)我需要从任意选择转移到真正的随机选择,同时拥有尽可能快的选择/更新查询和锁:@Erwin在具有密集插入/更新/删除(如队列)的表中这种方法不可靠。我想主要的问题是为planner收集的过时统计数据。我有“队列”表的thoughput大约为300项/秒。我将表的自动真空度调整为:autovacuum\u vacuum\u threshold=5000,autovacuum\u vacuum\u scale\u factor=0.0,autovacuum\u analysis\u scale\u threshold=5000,autovacuum\u analysis\u scale\u factor=0.0,在某些情况下,当队列包含大约100000项时,我会出现“共享内存不足”错误“真空满”对表修复问题有一段时间。@ OutkSoadrjdJ:我认为会话级锁和手动解锁可以解决这个特殊问题。请参阅新的<代码>跳过锁定第一。@ OrkkStRrdDJ:对于只有一个读取器(无并发)你根本不需要手动锁定。如果你仍然需要锁定,考虑设置的手册。考虑更有效的排队解决方案。如果还有疑问,我建议你提出一个新的问题,提供所有相关信息。评论不是地方。最好在DBA.SE上。
CREATE INDEX opportunities_deal_id ON opportunities (deal_id)
WHERE opportunity_available;