将 pg_try_advisory_xact_lock() 放入嵌套子查询中?
Put pg_try_advisory_xact_lock() in a nested subquery?
在我的 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"
)
深受 this related answer on dba.SE 启发。
但是这里 (Postgres pg_try_advisory_lock blocks all records) 他们说,如果我没记错的话,我 不应该在 WHERE
子句中使用 pg_try_advisory_lock()
因为我会在扫描的整个集合中每行调用一次一次(作为 where 子句中发生的过滤的一部分)。
我只想让我的查询找到并更新第一个(随机,LIMIT
)行 available = true
并将其更新为 available = false
,我需要锁定该行在这样做的同时,但没有发出新的请求等待释放以前的锁所以我添加了咨询锁 like suggested here.
我应该将 pg_try_advisory_lock()
放在 WHERE
子句之外吗?怎么做?
我用更多的解释和链接更新了我的参考答案。
在 Postgres 9.5(目前是测试版)中,新的 SKIP LOCKED
是一个更好的解决方案:
让我先简化一下您的查询:
直接查询
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 是一个布尔列,您可以将
opportunity_available = true
简化为 opportunity_available
- 子查询不需要return
*
,id
就够了。
通常,这会按原样工作。解释如下。
避免对不相关的行进行咨询锁定
可以肯定的是,您可以使用 OFFSET 0
hack(更少的开销)将所有谓词封装在 CTE 或子查询中 before 您应用 pg_try_advisory_xact_lock()
在下一个查询级别:
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_try_advisory_xact_lock(id)
将成为索引或位图索引扫描的过滤条件,并且只有符合条件的行才会被测试(并锁定)开始,因此您可以使用简单的形式而无需额外的嵌套。同时,您的查询性能得到了优化。我会那个。
即使一些不相关的行应该偶尔获得建议锁,这通常也没有关系。咨询锁只与实际使用咨询锁的查询相关。或者您是否真的有其他并发事务也使用咨询锁并针对相同 table 的其他行?真的吗?
唯一有问题的情况是如果大量不相关的行获得咨询锁,这只能在顺序扫描时发生,即使那样也不太可能发生。
在我的 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"
)
深受 this related answer on dba.SE 启发。
但是这里 (Postgres pg_try_advisory_lock blocks all records) 他们说,如果我没记错的话,我 不应该在 WHERE
子句中使用 pg_try_advisory_lock()
因为我会在扫描的整个集合中每行调用一次一次(作为 where 子句中发生的过滤的一部分)。
我只想让我的查询找到并更新第一个(随机,LIMIT
)行 available = true
并将其更新为 available = false
,我需要锁定该行在这样做的同时,但没有发出新的请求等待释放以前的锁所以我添加了咨询锁 like suggested here.
我应该将 pg_try_advisory_lock()
放在 WHERE
子句之外吗?怎么做?
我用更多的解释和链接更新了我的参考答案。
在 Postgres 9.5(目前是测试版)中,新的 SKIP LOCKED
是一个更好的解决方案:
让我先简化一下您的查询:
直接查询
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 是一个布尔列,您可以将
opportunity_available = true
简化为opportunity_available
- 子查询不需要return
*
,id
就够了。
通常,这会按原样工作。解释如下。
避免对不相关的行进行咨询锁定
可以肯定的是,您可以使用 OFFSET 0
hack(更少的开销)将所有谓词封装在 CTE 或子查询中 before 您应用 pg_try_advisory_xact_lock()
在下一个查询级别:
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_try_advisory_xact_lock(id)
将成为索引或位图索引扫描的过滤条件,并且只有符合条件的行才会被测试(并锁定)开始,因此您可以使用简单的形式而无需额外的嵌套。同时,您的查询性能得到了优化。我会那个。
即使一些不相关的行应该偶尔获得建议锁,这通常也没有关系。咨询锁只与实际使用咨询锁的查询相关。或者您是否真的有其他并发事务也使用咨询锁并针对相同 table 的其他行?真的吗?
唯一有问题的情况是如果大量不相关的行获得咨询锁,这只能在顺序扫描时发生,即使那样也不太可能发生。