回答

收藏

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

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

我有一个(Ruby on Rails 4)应用程序,并使用了一个PostgreSQL查询,该查询基本上查看机会表,在机会为’available =true’的行中随机搜索,并用’available = false’更新这些选定的行。每次用户单击“尝试机会”时,应用程序都会使用以下查询。
* F1 u/ W; Y7 F0 ~) eUPDATE opportunities s* u: s' S. X8 ?' S; ]& \, `
SET    opportunity_available = false
  K6 v* Q% ~3 s- v  T' _FROM  (
. o' [, |) e$ F" s& q   SELECT id
/ t0 i/ f3 I# A1 c- ^7 U   FROM   opportunities
* [9 k/ R: V! Z4 v   WHERE  deal_id = #{@deal.id}+ P5 t, w+ W. r+ l
   AND    opportunity_available
1 D6 ~. Q, i, f) P" Y   AND    pg_try_advisory_xact_lock(id)9 H7 `; \& |  L0 I& j9 k5 P2 F: q
   LIMIT  1
6 o1 ^. d2 O* f3 q: c   FOR    UPDATE
4 [5 ]5 g% i. [% Y7 l8 q; ^6 D   ) sub
( X  S6 N( i7 v. T* [WHERE     s.id = sub.id: G: h' t" v4 o2 y3 a
RETURNING s.prize_id, s.id;4 U) ]% v, ~: w  _& [, [
// inspired by https://stackoverflow.com/questions/33128531/put-pg-try-advisory-xact-lock-in-a-nested-subquery7 K' i6 |3 C* h
我已经苦苦挣扎了5天,但现在我已经设法大致了解了它的表现(严重):现在我需要知道如何对其进行更改。
  x7 \6 I' O' m& Z" H  t- ]/ s实际上,该应用程序“变得有点疯狂”,并完全按照我更新的最新版本进行选择(并更新该行)。
2 f! d4 M- C9 `8 Y4 K让我清楚我如何创造这些机会的过程  L6 g( }6 J6 B1 A- a
6 F9 V4 \: j) O' |5 F
我以管理员身份通过我的应用创建了20个机会(行)
2 A* `3 M) z8 }( S8 u) s1 w% I# t! b% v/ ?7 _
然后在管理面板中创建一个奖品,奖品的数量,比如说奖品id = 45,数量=4。该应用程序将进入“机会”表,并随机填充(这部分工作正常),其中有4行有priest_id = 45。
" w; q) L* L. P) R9 U0 ^0 X* V% f# H7 `' w3 U% T0 M: D% U
, |* E% n+ f% |2 m
现在,用户运行该应用程序,并且如上所述,该应用程序将不会随机选择一行,而是始终以最新的更新开始:似乎需要一行中有一个奖品,然后是另一个中有奖品,然后又是另一个,从不那些没有奖品的人(primed_id
1 |# w! G4 O3 J8 P- w( }5 B=空)…2 P; \- c8 i1 }' G; N) [' U& ~
现在,我进行了一些手动实验:我尝试手动更改表上的值(通过pgadmin),这是最奇怪的事情:如果我修改了第32行,然后是第45行,然后是第67行,当用户再次尝试播放时,猜测一下,则随机选择的行正是我按照相反顺序更新的行:它将依次选择第67行,第45行和第32行。" {) B4 j! ?8 ]3 z4 x; `8 ^
= true)。
" U. b# B) z5 ^" [- S我也尝试不用于更新或’pg_try_advisory_xact_lock(id)’行,看来它仍然有相同的问题。+ [, y! L5 P, t+ t& F2 q
作为管理员,我首先创建总共20行,然后创建4个获胜行,它们是最后要删除的行(即使在我的pgadmin屏幕上它们仍然位于同一行…也许在背景中,postgresql是将它们作为最后一次连续更新并选择它们吗?),这就是为什么一旦从这4个获胜行中选择了一个,然后所有其他行都跟随着的原因。
' w9 L) I# V3 K需要明确的是,我会逐行选择每个可用的机会(例如:按照我在pgadmin上看到的,第3行,然后第4行,然后第5行,因为行已经完全随机地分配了奖金)。问题是它没有这样做,而是经常连续接获所有获奖行…。
- B) E5 y) G* u, }8 u& L/ s2 `6 j我无语,也不知道如何打破这种模式。8 D% g' M! l/ O/ [
注意:这种模式并不是100%的时间连续发生,而是经常发生:例如,如果我有4个以上的获胜行,那么如果我持续按用户单击2分钟,它就会像此处所说的那样运行,然后停止并似乎(或者我可能是错的)表现正常,然后&分钟之后再次只会选择获胜的行…
4 V. c" c& Y: v$ N$ }编辑19 x% ^- B# h* {4 y+ i
这是将奖品注入到“机会”表中的方式(例如,我创建了一个id为21且该奖品数量为3的奖品)=>它将随机发送给他们(据我所知),但仅限于尚未有award_id的地方(即,如果机会有priest_id
) }; ~. o- J# S  n5 ~; B  l= empty,则可以将其放置在那里)
7 X) V$ ]& T5 R& c3 n+ `, j+ w  SQL (2.4ms)  1 [# L! w7 @; P/ E
  UPDATE "opportunities"+ e- c8 B, B0 `% T/ {
  SET "prize_id" = 21
2 w( w# V& Q% Y' y3 Y* x% |  WHERE "opportunities"."id"
8 o% g9 V: K  F5 v) Q4 L  IN (
& m' ~( m3 H" i, E; I( h& u/ J7 n    SELECT "opportunities"."id"0 _% A( f- D% s
    FROM "opportunities"
  k3 l$ Q( A% T( b( s: ~2 N    WHERE (deal_id = 341 AND prize_id IS NULL)
! B, f+ J" S4 E- c' p$ `/ M; h    ORDER BY RANDOM()8 y9 c$ G2 A; j$ ^, i% A
    LIMIT 3) //) ~! e. N; |7 Q, b
   (0.9ms)  COMMIT
! ^% t# z. `% u% E) q这个SQL查询是由Rails8 N5 h9 [$ d; S
gem(称为Randumb:github.com/spilliton/randumb)生成的。
9 Z' M6 ?- q8 d; F- L4 {               
* h" z- m3 x; Q( J解决方案:
! Q, N! D, l& V" x. I( a                3 v8 ~4 h, n! V
, |- ^7 e8 P. x8 Y7 ?9 P2 O) ~
9 _+ K* G7 S1 F* j/ D
                只是一个想法:与其调用random()而不是将其用作列的默认值(可以建立索引),一种类似的方法可以使用增量约为0.7 * INT_MAX的序列。
' R! j4 K4 C% t. y1 }\i tmp.sql
" ?* x6 i7 W. \3 FCREATE TABLE opportunities
* n6 K. S: A5 N1 [3 W    ( id SERIAL NOT NULL PRIMARY KEY
7 S( l6 ^/ d: J. @" C( v    , deal_id INTEGER NOT NULL DEFAULT 0" }( N+ i& g, i1 {
    , prize_id INTEGER- T$ B) ^3 B0 k2 z9 K0 n% w& q
    , opportunity_available boolean NOT NULL DEFAULT False- Q5 V2 v3 [4 h3 X/ M
            -- ----------------------------------------% R3 V3 m2 \1 O
            -- precomputed random() , (could be indexed)
; y% C5 i- F; |4 ^) X0 ~    , magic DOUBLE precision NOT NULL default RANDOM()& ?  O7 U4 n2 |( f0 ~
    );* S2 O# w* R( Z0 [
INSERT INTO opportunities(deal_id)/ K7 J9 Z: Y, `; f9 e
SELECT 341
- ~5 _6 h$ r# T; ?1 DFROM generate_series(1,20) gs
: a& L. B( m, M% t# u1 g    ;
. E0 r) H' H, EVACUUM ANALYZE opportunities;
1 B. Q1 ?; t, s" k! yPREPARE add_three (integer) AS (: j* Z* i1 d, B
WITH zzz AS (0 V4 j, W7 k( F
  UPDATE opportunities
6 i4 ?' s3 Y. i/ R# a. I  SET prize_id = 219 Y# z+ K0 ^2 \$ \- L2 A8 X
    , opportunity_available = True2 w: o2 j2 d% B5 O
    -- updating magic is not *really* needed here ...
% r3 {: v; c  S( y8 A6 b: U    , magic = random()' w) g' ^$ T9 L/ d" [/ S( F
  WHERE opportunities.id6 G; ?7 z. P- h% y. G7 [
  IN (
" e/ _2 F- i% o& U" W7 c0 k    SELECT opportunities.id
9 M: I2 k3 k2 `- W4 H# b# h    FROM opportunities7 n) t) p+ J- b/ a9 O$ R6 [
    WHERE (deal_id = $1 AND prize_id IS NULL)
9 K  X0 B$ m( u$ n  d' u) Y  H    -- ORDER BY RANDOM(): A3 H0 z" Z! O. f. e
    ORDER BY magic' b8 `- b/ f7 C& K
    LIMIT 3)
# J4 Y% p2 f* @5 y) y- O0 @RETURNING id, magic
+ m# E0 {2 k1 Y2 a( L& t    ) --
- f2 O- a$ l- @+ Y7 hSELECT * FROM zzz8 r6 q+ s+ x6 c" }" ]! ^8 _
    );  s9 d8 {9 K3 A
PREPARE draw_one (integer) AS (! I3 [9 r: h  W* [( S- E7 |
  WITH upd AS (( [7 |, u& N9 Q/ S, ~
  UPDATE opportunities s2 Z, `, _/ @! x/ q
  SET    opportunity_available = false
) R9 ?, d0 o6 z+ r8 R7 L6 y: _  FROM  (
. q* n  p: Q" q     SELECT id
. l5 J4 A1 B6 S7 w( _3 W  Q5 J     FROM   opportunities
0 @' ?+ K# g+ Y) U     WHERE  deal_id = $1
- c) i* Y- e; @2 b4 }$ D, C     AND    opportunity_available
' F6 \* i: }. O; f2 p     AND    pg_try_advisory_xact_lock(id)" g2 w! n3 r! I: r
     ORDER BY magic6 l( D2 }" ^/ x/ u( z: b
     LIMIT  1
: r. o8 S" g: O* f9 a     FOR    UPDATE
- J9 c& ~" s, g* B. P8 l1 V% Y     ) sub- o# u, D' \5 U; i3 |. B
  WHERE     s.id = sub.id& }) l3 E5 e4 s
  RETURNING s.prize_id, s.id, magic/ v9 `! E$ j' C& M5 P
    )! _. ]; N; v+ V  t0 v$ @- b: T
SELECT * FROM upd
' h7 H0 S9 J( }# E/ l    );
; q- [# k* b; b6 Q/ USELECT * FROM opportunities;( n2 E+ Q) E* e- H* v0 Z) {* a
\echo add3
3 j9 j" x( w/ p1 [: sEXECUTE add_three(341);" Z& ~. w8 _1 t0 @1 n
SELECT * FROM opportunities;3 S9 `9 h1 Z0 n2 `
\echo add3 more! }) b& p3 @. W; Z
EXECUTE add_three(341);
6 R5 l# _3 [6 h* J3 G2 q: sSELECT * FROM opportunities;
7 M0 a, q/ j3 d9 v' ^\echo draw1
( Z& p, n4 Z4 AEXECUTE draw_one(341);
7 \3 f# _, t- D7 Q( J% F" QSELECT * FROM opportunities;) V' I/ `6 p- v* v8 M; R! }0 Y
\echo draw2
  f" y# B" A, OEXECUTE draw_one(341);3 o" ]6 v2 D' @
SELECT * FROM opportunities;: P8 m, P9 ?! |2 S) X" g* l' N2 n
VACUUM ANALYZE opportunities;
- \' j4 i, u& ^$ V2 p\echo draw35 S, P7 I  S0 {+ I
EXECUTE draw_one(341);1 `+ a8 |9 j3 k: a% _
SELECT * FROM opportunities;
! w3 _5 W3 R2 r\echo draw4' ^7 A) T7 W6 q& F! {3 {
EXECUTE draw_one(341);2 F) |8 i. P. u
SELECT * FROM opportunities;
分享到:
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则