回答

收藏

postgresql 9.4-阻止应用始终选择最新更新的行

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

我有一个(Ruby on Rails 4)应用程序,并使用了一个PostgreSQL查询,该查询基本上查看机会表,在机会为’available =true’的行中随机搜索,并用’available = false’更新这些选定的行。每次用户单击“尝试机会”时,应用程序都会使用以下查询。) E8 p+ K* a3 P
UPDATE opportunities s8 b% A! o  l( x
SET    opportunity_available = false# ~' z, [& b8 t9 o
FROM  (
1 B7 ]7 x& K( `0 z1 A" G' O! g   SELECT id: U, g, N: w: m3 G# j' \1 v
   FROM   opportunities$ Y" [9 W) J3 Y7 }% j2 z7 V7 z
   WHERE  deal_id = #{@deal.id}
/ @% z$ J7 A& G, f& e   AND    opportunity_available
0 E3 K+ b: O$ @   AND    pg_try_advisory_xact_lock(id)
5 a& A  X% }2 L: `   LIMIT  1! r/ C. @. w0 t* D
   FOR    UPDATE
/ w# f3 g2 Q5 {0 ?( ?   ) sub( p1 c  _& e: q+ V3 B
WHERE     s.id = sub.id" E, o0 c8 t" k5 M& C9 Q% b9 I2 _
RETURNING s.prize_id, s.id;1 F) W8 }* @! h9 S1 e2 K3 ^9 l
// inspired by https://stackoverflow.com/questions/33128531/put-pg-try-advisory-xact-lock-in-a-nested-subquery
; O# }6 W7 y& Y' ~. E+ U2 D5 i我已经苦苦挣扎了5天,但现在我已经设法大致了解了它的表现(严重):现在我需要知道如何对其进行更改。2 m& T7 }5 M8 i: o; i
实际上,该应用程序“变得有点疯狂”,并完全按照我更新的最新版本进行选择(并更新该行)。
3 n) m5 l* o6 v7 `$ i+ e# Y让我清楚我如何创造这些机会的过程9 {: j( G. O0 V1 E) }3 K; r9 Z. f8 R: Q

. D4 r! K) l9 o5 |. e我以管理员身份通过我的应用创建了20个机会(行)
* E6 o) G- Q1 R0 m$ Y- [/ v" S: _8 F4 _6 R
然后在管理面板中创建一个奖品,奖品的数量,比如说奖品id = 45,数量=4。该应用程序将进入“机会”表,并随机填充(这部分工作正常),其中有4行有priest_id = 45。
% J8 C" |7 c/ G% L
! r+ ^  |' D8 P; `- \$ P7 X

, O0 E; h& S; T& z现在,用户运行该应用程序,并且如上所述,该应用程序将不会随机选择一行,而是始终以最新的更新开始:似乎需要一行中有一个奖品,然后是另一个中有奖品,然后又是另一个,从不那些没有奖品的人(primed_id
+ Q8 {4 `8 i# R8 \- N5 c=空)…" _5 {. {7 W. f) L) w+ V5 l8 f2 R
现在,我进行了一些手动实验:我尝试手动更改表上的值(通过pgadmin),这是最奇怪的事情:如果我修改了第32行,然后是第45行,然后是第67行,当用户再次尝试播放时,猜测一下,则随机选择的行正是我按照相反顺序更新的行:它将依次选择第67行,第45行和第32行。
5 x( m4 R3 G: l/ i. ], O+ l+ z= true)。
8 Q6 F+ L! \, d" g我也尝试不用于更新或’pg_try_advisory_xact_lock(id)’行,看来它仍然有相同的问题。
* k" r) U. u2 v作为管理员,我首先创建总共20行,然后创建4个获胜行,它们是最后要删除的行(即使在我的pgadmin屏幕上它们仍然位于同一行…也许在背景中,postgresql是将它们作为最后一次连续更新并选择它们吗?),这就是为什么一旦从这4个获胜行中选择了一个,然后所有其他行都跟随着的原因。7 ~; h9 D6 K8 z$ I1 E' v( D
需要明确的是,我会逐行选择每个可用的机会(例如:按照我在pgadmin上看到的,第3行,然后第4行,然后第5行,因为行已经完全随机地分配了奖金)。问题是它没有这样做,而是经常连续接获所有获奖行…。
3 b' u! ?+ F/ p! w5 {我无语,也不知道如何打破这种模式。
8 \0 T! o, t: k5 ~注意:这种模式并不是100%的时间连续发生,而是经常发生:例如,如果我有4个以上的获胜行,那么如果我持续按用户单击2分钟,它就会像此处所说的那样运行,然后停止并似乎(或者我可能是错的)表现正常,然后&分钟之后再次只会选择获胜的行…
/ X& i+ ]$ l4 \7 _, t. L编辑1
) g( i/ J1 F6 x4 f这是将奖品注入到“机会”表中的方式(例如,我创建了一个id为21且该奖品数量为3的奖品)=>它将随机发送给他们(据我所知),但仅限于尚未有award_id的地方(即,如果机会有priest_id
* H( x% J, H0 M& \- t" t: M5 d= empty,则可以将其放置在那里)
, u) e: p4 m4 w5 r  SQL (2.4ms)  ; Q, p% \8 D4 P. q2 i; J
  UPDATE "opportunities"3 Y* ~; u" Y9 L$ C2 H4 [
  SET "prize_id" = 212 ^5 _8 [( k( F/ t
  WHERE "opportunities"."id" $ l8 M- f1 E4 a% F7 V
  IN (0 R2 X: \; ]6 V; m
    SELECT "opportunities"."id"
; K5 m  a# C& L4 q    FROM "opportunities"
- I4 U% {( V) `; _2 j3 c    WHERE (deal_id = 341 AND prize_id IS NULL)/ d6 D" ~6 }8 N8 e3 c9 R9 \( \
    ORDER BY RANDOM()# [4 s' T  T( Y  z* f; l- G0 [
    LIMIT 3) //5 [; [- _) T  a" s
   (0.9ms)  COMMIT6 X* [$ S  a' q: i
这个SQL查询是由Rails
% w! a! W* q8 x/ K0 W+ I+ ?gem(称为Randumb:github.com/spilliton/randumb)生成的。$ j" U8 Z7 O7 P3 n2 C* r) T# b
                ! ^9 W( _: N  E" N* g/ b5 V5 z
解决方案:# w+ K2 R8 o9 O, c
               
( l$ R, `7 W% P
' w9 B8 D! B" e5 f( q
4 N5 C* l* d1 g6 z; g                只是一个想法:与其调用random()而不是将其用作列的默认值(可以建立索引),一种类似的方法可以使用增量约为0.7 * INT_MAX的序列。/ L0 d7 n: ~4 ~% N1 X
\i tmp.sql; j4 \3 u3 b/ n
CREATE TABLE opportunities
( y3 p7 y/ E; K7 |+ d    ( id SERIAL NOT NULL PRIMARY KEY* B) ^; f1 q$ f6 S) ^! s* w
    , deal_id INTEGER NOT NULL DEFAULT 08 j2 ~# Y' m" A6 c  r; z8 M
    , prize_id INTEGER
$ W$ k9 V; y- r+ U0 j: q    , opportunity_available boolean NOT NULL DEFAULT False3 ]; Z1 A1 O! W8 A2 f/ M: W
            -- ----------------------------------------
" {# a& p* O  N' s- N            -- precomputed random() , (could be indexed)
  |' p1 C# X! [    , magic DOUBLE precision NOT NULL default RANDOM()
5 P+ h% r' \  _* w    );
4 Z2 U0 l# h0 @3 ]INSERT INTO opportunities(deal_id)- t) _. d( Z; g' U1 Y$ e
SELECT 3417 c( M* p2 S$ t
FROM generate_series(1,20) gs
- x0 T; R! F6 y/ [2 [    ;
1 c( Q) g+ u6 [! H/ \+ @VACUUM ANALYZE opportunities;
2 p- z/ k+ n2 `7 \0 qPREPARE add_three (integer) AS (
1 f7 ]3 ]4 T( ^# f4 m4 j3 F* ]3 RWITH zzz AS (6 B. G  j+ @9 p. s# ?0 ]
  UPDATE opportunities* x8 {) C( U+ ^
  SET prize_id = 21
' S1 |. Z' T) G    , opportunity_available = True
) F0 v5 V; h. z" \( ?: m" p    -- updating magic is not *really* needed here ...
9 ?6 |* b4 Q+ Q9 Q$ B: s    , magic = random()
0 s& K. t# W9 R7 A$ }% m9 z  WHERE opportunities.id) N- U! w* t: q1 B6 v, `
  IN (! o: S6 v: B2 A( t0 L8 B9 I) |- G
    SELECT opportunities.id) D9 L! e) L1 y4 W
    FROM opportunities
* E8 n/ |0 `/ A) U0 _    WHERE (deal_id = $1 AND prize_id IS NULL)
$ y; P" Y5 |4 n4 r6 _    -- ORDER BY RANDOM()3 W! @4 ^! p- F; x
    ORDER BY magic
+ A4 z/ |2 ~" r* J$ c; f% X- C    LIMIT 3)
7 q# v2 R8 X1 c7 Y3 pRETURNING id, magic
7 v. j' D9 Z6 ~" ?! t    ) --
, U" D9 y  Y6 _4 DSELECT * FROM zzz
5 \/ F! i$ q$ B8 u4 q# D5 @    );
( C0 S1 U- z! B0 U. G' vPREPARE draw_one (integer) AS (
8 a7 o: f0 D' l, N/ {0 o  WITH upd AS (
7 N* C* A! h$ |$ Q( c) ~  UPDATE opportunities s6 k) S3 V* ?# B; c
  SET    opportunity_available = false
1 L; F2 s0 b/ v  FROM  (; s  ?& C. E% H( @( a0 v2 F  g6 k
     SELECT id" u" r2 ]5 v( l2 [, T5 t/ ~& Y
     FROM   opportunities
7 p' W/ s4 W/ |     WHERE  deal_id = $19 O* e/ N" }0 l( C9 ?5 b
     AND    opportunity_available1 {; \+ d; @0 a. Q! q8 ^
     AND    pg_try_advisory_xact_lock(id)
- _" L% n( K+ Z     ORDER BY magic6 m: U7 o, T. r8 o( Q
     LIMIT  15 ?  X. Y& B" K: e
     FOR    UPDATE
5 h& z3 p" b* M     ) sub  o* g  ~) a* `$ w
  WHERE     s.id = sub.id8 n0 n+ W( G& p; V5 s  H
  RETURNING s.prize_id, s.id, magic
, d4 m2 R+ [5 Y  Q) f* C    )
& k- \3 j. Z+ w+ Q' gSELECT * FROM upd
; G; c" v( j3 f# k) b6 ~+ V    );; ~! w8 r9 ^( H& s, s$ y( c
SELECT * FROM opportunities;
) m! v( D& D. N: F- ?" t\echo add3
7 A. y. r0 L6 |+ q# \. o; N2 U  UEXECUTE add_three(341);
# f- c! e; {5 j( f1 }. USELECT * FROM opportunities;
% ~' l2 H8 w% O* ~. q2 X1 r/ o\echo add3 more/ E3 f- A8 |: Z' d
EXECUTE add_three(341);% O% H- R) B5 l: M8 H, T3 D
SELECT * FROM opportunities;
( ^! n3 ?. W* q1 _\echo draw1( G1 ]/ S; T0 N$ T$ K
EXECUTE draw_one(341);5 N5 y" C( c, L
SELECT * FROM opportunities;% Z9 Q6 y9 _) m
\echo draw2! c0 ~& O5 E* h* U3 I* q
EXECUTE draw_one(341);9 C2 H4 s0 x4 G$ w6 O$ g9 a
SELECT * FROM opportunities;
! F2 Y/ T. b5 ?* [2 DVACUUM ANALYZE opportunities;. S  Z- N6 A' Z  ]
\echo draw3
/ Z9 }8 d  w8 `EXECUTE draw_one(341);& F6 k8 W- {" ?% _3 t
SELECT * FROM opportunities;
9 C4 s8 z4 L5 z  B- v9 |9 R\echo draw4
0 Q1 R' @. w- p" |% X+ cEXECUTE draw_one(341);
& O# L- I( f5 X  ]2 ?+ q  ySELECT * FROM opportunities;
分享到:
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则