回答

收藏

更新竞赛条件Postgres的位置(已读)

技术问答 技术问答 182 人阅读 | 0 人回复 | 2023-09-14

我正在尝试编写一个查询,仅当用户打开的活动声明不超过两个时,才将“声明”表中的行更新为活动状态。因此,它是用于数据的完整性非常重要的是,用户 永远4 W) }, `; _! W  S/ B4 V
有两个以上的主动要求在任何给定的时间打开。$ S: k8 |1 j" X  T: ~; J
我在并发环境中运行此查询,因此两个进程可能同时执行此查询。我也在默认Read Committed隔离级别下运行它。
$ e) n' B( r. O% W7 Y我想知道是否由于子选择和更新子句之间的竞争条件而在某个时候打开一个用户有两个以上的主动声明的可能性。7 ]) \0 A" {& o8 m, v$ C
值得一提的是,对于此查询,性能几乎不如数据完整性那么重要。4 l, _7 F1 r+ R; E( |' ?- \. p+ C; s
update claim  K. B2 N; k0 |. {, i6 w9 L
set is_active = '1'
7 o2 W. G) @$ w" T4 kwhere claim.id = %s
4 o/ n; o- Z  b- ]# c; j2 W# @and (2 > (select count(*)
1 _# Y$ |0 m  x          from claim as active_claim
" T6 @: b5 k. }          where active_claim.user_id = %s, E& v3 C/ j9 U2 g; w9 q, ]
          and active_claim.is_active = '1'))) ~: L) P) M) B& _/ u. D, u
               
7 T" v( B1 I9 C2 J& j" f6 e7 G0 H解决方案:4 `2 ^" C* [; L7 j# D
                1 r) Y) s" ^- ?9 c- J

9 {6 d: k' V0 g1 a& j; B+ P& l' J7 `# A" i/ N* x4 T
                是的,这绝对有可能导致两个以上的活动声明,因为并发事务无法看到彼此的更改,因此两个或多个并发执行将同时看到2个声明,并且都将继续更新其目标声明以使它们积极的。
- i# U+ u+ M- @# a( W/ J5 b6 L3 G餐桌锁
% c9 ^% w0 r3 ?: p最简单的选择是:/ Q9 r6 r) {2 z* a; N
BEGIN;2 R% F4 l% i3 n" [% H
LOCK TABLE claim IN EXCLUSIVE MODE;$ y6 L( W: E9 ?9 n! w' y
UPDATE ...
6 q' b5 G' v5 e8 V, ^COMMIT;, x0 v& Z1 Z" n" h3 v' ^; t
…但这是一个非常沉重的解决方案。1 p! h. o/ @* x" m2 |! p, M
用户对象上的行级锁定
) T( O! E) q4 s3 w) `: N* m假设您有一张user声明所有者的表格,则应改为:6 z: _) M4 m& W. W- w
SELECT 1 FROM user WHERE user_id = whatever FOR UPDATE) d* c, Q) J! X2 ~
在同一笔交易中,在运行您的之前UPDATE。这样,您将在用户上持有排他的行锁,而其他SELECT ... FOR
: y' r/ Q# Z" w) g# d& [UPDATE语句将在您的锁上阻塞。此锁还将阻止UPDATEs和删除user;如果没有or子句,它将 不会 阻止SELECT用户的plain
0 l! G$ q2 D3 Y6 R/ l! Ts 。FOR UPDATE``FOR SHARE
/ A+ u: P/ d, a请参见PostgreSQL手册中的显式锁定。3 ^( [$ c6 I# f' x: M) a/ d) m3 S
SERIALIZABLE 隔离! m1 ], O# p" X8 g! ^; W, F( g" c
另一种方法是使用SERIALIZABLE隔离。PostgreSQL
% e5 n. G  g: J5 W9.2及更高版本具有事务相关性检测功能,在上面给出的示例中,这将导致所有冲突事务(只有一个冲突事务)中止并发生序列化失败。因此,您的应用必须记住启动事务时尝试执行的操作,并能够捕获错误,检测到错误是序列化失败,并在序列化失败后重试。7 `2 j8 S+ o  w  M6 Z: d7 o  w2 S& h
请参见PostgreSQL手册中的事务隔离。' o# q6 q( e4 |7 {+ R
咨询锁
$ R% t1 K) V" v5 z有时,没有好的候选对象需要进行行锁定,并且由于某种原因或其他可序列化的隔离措施无法解决该问题或由于其他原因而无法使用。事实并非如此,这只是一般信息。
. ]0 C$ N/ O& a) b# X在这种情况下,您可以使用PostgreSQL的咨询锁来锁定任意数值。在这种情况下,您会pg_advisory_xact_lock(active_claim.user_id)举个例子。显式锁定章节提供了更多信息。
分享到:
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则