[英]Put pg_try_advisory_xact_lock() in a nested subquery?
In my Ruby on Rails 4 app, I have this query to a Postgres 9.4 database: 在我的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"
)
Very much inspired by this related answer on dba.SE . dba.SE上的相关答案极大地启发了我们 。
But here ( Postgres pg_try_advisory_lock blocks all records ) they say, if I'm not mistaken, that I should not use pg_try_advisory_lock()
inside the WHERE
clause because I would be calling it once per row in the entire set that gets scanned (as part of the filtering that occurs in the where clause). 但是在这里( Postgres pg_try_advisory_lock阻止了所有记录 ),他们说,如果我没记错的话,我不应该在WHERE
子句中使用pg_try_advisory_lock()
,因为我将在被扫描的整个集中的每一行调用一次它。在where子句中进行的过滤)。
I just want my query to find and update the first (randomly, with LIMIT
) row where available = true
and update it to available = false
, and I need to lock the row while doing this, but without making new requests waiting for the release of the previous lock so I added advisory locks like suggested here . 我只想让我的查询查找并更新第一行(使用LIMIT
随机地),其中available = true
并将其更新为available = false
,我需要在执行此操作时锁定该行,但无需等待新的请求来等待发布之前的锁中的一个,因此我添加了这里建议的咨询锁。
Should I place pg_try_advisory_lock()
outside the WHERE
clause? 我应该将pg_try_advisory_lock()
放在WHERE
子句之外吗? How to do it? 怎么做?
I updated my referenced answer with more explanation and links. 我更新了参考答案,并提供了更多解释和链接。
In Postgres 9.5 (currently beta) the new SKIP LOCKED
is a superior solution: 在Postgres 9.5(当前为beta)中,新的“ SKIP LOCKED
是一种出色的解决方案:
Let me simplify a few things in your query first: 让我先简化一下查询中的几件事:
UPDATE opportunities s
SET opportunity_available = false
FROM (
SELECT id
FROM opportunities
WHERE deal_id = #{@deal.id}
AND opportunity_available
AND pg_try_advisory_xact_lock(id)
LIMIT 1
FOR UPDATE
) sub
WHERE s.id = sub.id
RETURNING s.prize_id, s.id;
opportunity_available = true
to just opportunity_available
由于possible_available是一个布尔列,因此您可以将opportation_available opportunity_available = true
简化为opportunity_available
*
from the subquery, just id
is enough. 您不需要从子查询中返回*
,只需id
就足够了。 Typically, this works as is . 通常,这是按原样工作的 。 Explanation below. 以下说明。
To be sure, you could encapsulate all predicates in a CTE or a subquery with the OFFSET 0
hack (less overhead) before you apply pg_try_advisory_xact_lock()
in the next query level: 可以肯定的是,您可以在下一个查询级别应用pg_try_advisory_xact_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;
However , this is typically much more expensive. 但是 ,这通常要贵得多。
There aren't going to be any "collateral" advisory locks if you base your query on an index covering all predicates, like this partial index: 如果您将查询基于涵盖所有谓词的索引(例如此部分索引),则不会有任何“附带”咨询锁:
CREATE INDEX opportunities_deal_id ON opportunities (deal_id)
WHERE opportunity_available;
Check with EXPLAIN
to verify Postgres actually uses the index. 与EXPLAIN
一起检查以验证Postgres实际使用了索引。 This way, pg_try_advisory_xact_lock(id)
will be a filter condition to the index or bitmap index scan and only qualifying rows are going to be tested (and locked) to begin with, so you can use the simple form without additional nesting. 这样, pg_try_advisory_xact_lock(id)
将成为索引或位图索引扫描的过滤条件,并且只有合格的行才开始进行测试(并锁定),因此您可以使用简单的表单而无需其他嵌套。 At the same time, your query performance is optimized. 同时,您的查询性能得到了优化。 I would do that . 我会做到这一点 。
Even if a couple of unrelated rows should get an advisory lock once in a while, that typically just doesn't matter. 即使几个不相关的行偶尔会获得咨询锁,通常也没关系。 Advisory locks are only relevant to queries that actually use advisory locks. 咨询锁仅与实际使用咨询锁的查询有关。 Or do you really have other concurrent transactions that also use advisory locks and target other rows of the same table? 还是您真的有其他并发事务也使用咨询锁并定位同一表的其他行? Really? 真?
The only other problematic case would be if massive amounts of unrelated rows get advisory locks, which can only happen with a sequential scan and is very unlikely even then. 唯一有问题的情况是,如果大量无关的行都获得了咨询锁,这只能在顺序扫描中发生,即使在那时也不太可能。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.