回答

收藏

将pg_try_advisory_xact_lock()放在嵌套的子查询中?

技术问答 技术问答 49 人阅读 | 0 人回复 | 2023-09-13

在我的Ruby on Rails 4应用程序中,我有以下查询到Postgres 9.4数据库:
) V. w1 j) z3 B: i$ L@chosen_opportunity = Opportunity.find_by_sql(: f& n3 M9 n. J, ?/ B1 A* @
  " UPDATE \"opportunities\" s$ a7 R; T: y& `. {2 y5 m
    SET opportunity_available = false
# s: |' H/ K$ K2 C( f    FROM (4 T' r9 ~. p! v7 p$ w3 t" w
          SELECT \"opportunities\".*; w; P/ s6 W) y  Z
          FROM   \"opportunities\"
+ i' }, I0 E9 N! ?5 U          WHERE  ( deal_id = #{@deal.id}
/ E* L4 a) G. B2 a, E          AND    opportunity_available = true
! S' l( H  h8 z8 r          AND    pg_try_advisory_xact_lock(id) )3 a* L6 U. o6 S
          LIMIT  1
. ?/ v3 N8 [4 j& i" h9 f          FOR    UPDATE5 `6 W% [% j$ {3 D; ?: ]
          ) sub: ~4 C# Z: W. T6 ]0 e" \
    WHERE       s.id = sub.id; U  i; L* h. b* l: g2 l) k4 O
    RETURNING   sub.prize_id, sub.id"4 R$ z/ G5 ~. f8 ^# N4 {8 |
)& U6 y8 o$ c+ `( L& g' s
但是在这里Postgrespg_try_advisory_lock阻止了所有记录他们说,如果我没记错的话,我不8 D, b- Y5 u* b
应该pg_try_advisory_lock()在WHERE子句中使用,因为我将在被扫描的整个集合中的 每一行 调用 一次3 a+ j9 t; }; A
(作为过滤的一部分)出现在where子句中)。
# _1 H1 {" n! i8 }" D6 p我只希望查询在其中找到并更新第一行(随机地LIMIT),available = true并将其更新为available =
& A- y3 k  B/ d0 O  Xfalse,并且我需要在执行此操作时锁定该行,但无需发出新请求来等待先前锁的释放,因此我添加了像这里建议的咨询锁。
. D+ L+ Q7 x: s% Q/ d& t! }' [$ p我应该放在条款pg_try_advisory_lock()之外WHERE吗?怎么做?
; y- B) ~: c/ B9 h7 t                % ?+ \9 q; }& _9 _1 M
解决方案:
, k8 F1 P9 N" L+ \: |% P                . X( }& {: u  S3 k

+ D2 k( K- ~- @  O( ]
' o. B3 F5 J2 F, D6 V                我更新了参考答案,并提供了更多解释和链接。
5 {1 f& N" D7 r6 g- Y  N$ l) {5 D在Postgres 9.5(目前为beta)中,新功能SKIP LOCKED是一种出色的解决方案:/ @: z. z# S) c) |
Postgres UPDATE-LIMIT 1
' j1 Q) B1 `, W  }

+ D3 g4 b1 n; U0 ~  ~首先让我简化一下查询中的几件事:
& V; `4 E, k% c  c% ~5 h直接查询& h$ v0 C0 l$ Z+ e
UPDATE opportunities s) @. m: I9 T' v) ~
SET    opportunity_available = false
$ ^8 z6 A) l+ N, M! `# K5 Z$ P& ZFROM  (* R0 E# T5 ?7 [* R
   SELECT id
, P6 ~1 B1 N- m$ V8 X   FROM   opportunities
. k2 e# X: u, c- s, l7 ]5 `   WHERE  deal_id = #{@deal.id}
  n  N* i- Z: e( W! r4 n3 a   AND    opportunity_available
# \6 E; C/ S! e( @   AND    pg_try_advisory_xact_lock(id)) _- u0 e& |9 N9 ^  K3 \5 ^8 b' i
   LIMIT  1+ ]2 R2 M' O; o
   FOR    UPDATE
$ b# \% H* u% b& L4 B5 o- @5 m! J   ) sub
& N$ m+ T7 e" e: TWHERE     s.id = sub.id
/ z1 i/ v, r# E  ]- B9 GRETURNING s.prize_id, s.id;
' @3 o7 v0 X4 e1 ]所有的双引号都带有合法的小写字母名称。1 U/ R$ E$ G' f8 p( g' w5 E) U
由于career_available是一个布尔列,因此您可以简化opportunity_available = true为opportunity_available
- C, j3 s) I! o3 f您不需要*从子查询返回,就id足够了。& y+ Y# e/ d/ N/ n; Q& H4 C% ?1 X

$ O6 ^  X, H  j通常,它 按原样 工作。解释如下。
. ?* P  a5 p- z6 N2 M避免对不相关的行进行咨询性锁定0 N  y0 Q/ q9 e3 V5 a: U
可以肯定的是, 在 应用到下一个查询级别 之前 ,您可以将所有谓词封装在带有OFFSET 0黑客的CTE或子查询中(减少开销):
! E8 L& ~8 [0 W; Z__pg_try_advisory_xact_lock()# h( d8 c# p; V5 r, g
UPDATE opportunities s
1 x+ M8 p! k' G2 ZSET    opportunity_available = false
) r$ D+ \- ^2 ?; O  ~FROM (
# S% }+ Z" u, Q$ H   SELECT id' ]0 Q5 K1 L3 \4 O- b$ w
   FROM  ( . R% |9 t3 i' s, P( W) B
      SELECT id0 y" m3 i8 z# C
      FROM   opportunities2 b0 U( J2 |/ o! K
      WHERE  deal_id = #{@deal.id}
2 \/ H6 \" q% C; O# H4 d      AND    opportunity_available
8 R7 G1 }6 Q5 e- w9 l/ R      AND    pg_try_advisory_xact_lock(id)
! j' t4 ?. Y1 E      OFFSET 01 O2 T; O) q! D* D4 e8 M! K2 E
      ) sub1
! E/ m0 E6 z, Q. X% t) P0 X7 c4 i   WHERE  pg_try_advisory_xact_lock(id)% x8 S, ?" V/ J8 b
   LIMIT  18 p( N- \5 C: c8 G! Z
   FOR    UPDATE( A4 W" \1 J2 A1 j0 Q2 w4 _! Q. S
   ) sub2
! v. x; Z) A% q" rWHERE     s.id = sub.id
+ J5 \" j  `+ {, ~! fRETURNING s.prize_id, s.id;
& o, `' f5 |8 e/ {但是 ,这通常要贵??得多。
* D( B. B/ [, b你可能不需要这个: u, F- J" T4 z% J1 x, L2 }& n! g
如果您将查询基于覆盖所有谓词的索引(例如,部分索引),则不会有任何“附带”咨询锁:
- C; t) D2 y1 M( uCREATE INDEX opportunities_deal_id ON opportunities (deal_id)7 }- O9 ~' f; q' R
WHERE opportunity_available;
  B/ c/ Q) w! Y7 D  b, I4 X' J6 }3 r  {检查EXPLAIN以验证Postgres实际使用该索引。这样,pg_try_advisory_xact_lock(id)将成为索引或位图索引扫描的筛选条件,并且仅将对合格行进行测试(并锁定),因此您可以使用简单的表单而无需其他嵌套。同时,您的查询性能得到了优化。我会做
& a4 V2 V5 i" [2 [0 f5 r# v那 。
' t) r" _2 _9 p( u9 e即使 几个不相关的行 _ 偶尔_
% v, Y3 d( W( O0 n1 q: h会获得咨询锁,通常也没关系。咨询锁仅与实际使用咨询锁的查询有关。还是您真的有其他并发事务也使用咨询锁并定位同一表的其他行?真的吗?3 s6 m. e% A5 z
唯一有问题的情况是,如果大量不相关的行获得了咨询锁,那么只有在顺序扫描时才会发生这种情况,即使在那时也不太可能。
分享到:
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则