处理ON INSERT触发器时如何锁定innodb表?
技术问答
349 人阅读
|
0 人回复
|
2023-09-14
|
我有两个innodb表:3 [, G+ Z( r& s M, o2 O
文章7 u+ M, I2 D8 ?0 S- }
id | title | sum_votes1 c- u- W7 L# a9 t0 u
------------------------------
6 i4 N7 H2 O- | I1 W) v2 X3 ^1 | art 1 | 5. g1 C0 Y2 W) u8 Y1 \! N
2 | art 2 | 85 p9 ]( b$ N* n4 y4 `1 }
3 | art 3 | 35
- y" j4 v& ?3 w9 |! O1 ?7 D票数
9 d; Y) E8 t: R" Y% O+ `! Hid | article_id | vote: F( N$ q% _" ~3 ]3 e, `- q n6 n# ~
------------------------------) w7 l) z2 h& k0 x2 [
1 | 1 | 1
$ |; [: a, C% p* ]$ y7 r2 V2 | 1 | 24 E1 [, C9 @5 |1 p! Q! j+ ]( b
3 | 1 | 2/ d* u: R% O8 {& R1 U6 s2 T+ Y
4 | 2 | 10
6 ~% I4 G4 V% X5 | 2 | -2
- Q; d( u$ \6 @! t6 | 3 | 10
2 a/ M- l6 {) q" ?! f! R; X7 | 3 | 15! V9 j: r" u, g7 b
8 | 3 | 12; @ v. ]+ ]# H0 _1 q4 J, u
9 | 3 | -2
6 y) t/ m$ E9 z将新记录插入votes表中时,我想通过计算所有投票的总和来更新表中的sum_votes字段articles。3 I$ E" c9 G; Q% w& ^8 n. e+ r
问题* h; f+ O/ e' ?# F4 S/ i
如果SUM()计算本身非常votes繁琐(表有700K条记录),则哪种方法更有效。3 V) `) a* R( j' A
1.创建触发器7 `7 _! }, ^& x) G
CREATE TRIGGER `views_on_insert`: A0 z2 {! x& E, M2 b
AFTER INSERT
* S" b8 V5 S2 B" h" R# P6 \ON `votes`4 }0 Y' p% Z! L u& ?* o0 S: o
FOR EACH ROW
: R7 K4 d; y BBEGIN
2 z6 D" y. ?3 e UPDATE `articles` SET
l% N: O7 J9 L5 n+ K- D' M* A sum_votes = (* q! y1 C+ W! `# N7 l- M9 F
SELECT SUM(`vote`)
0 Q, Z! o+ U) `- p7 T( I. b( e% O FROM `votes`
i6 _# ~; s- J8 @0 S5 F5 R WHERE `id` = NEW.article_id9 B3 M+ X1 n4 Y' t7 d: U$ x+ O1 p' ], z
)
/ ^# ?) w# r/ I1 J WHERE `id` = NEW.article_id;
7 E3 h# G0 B# p- D# j+ BEND;
$ p" t3 i3 }3 D& F2.在我的应用程序中使用两个查询4 @+ {& L4 p3 L2 j5 R) G
SELECT SUM(`vote`) FROM `votes` WHERE `article_id` = 1;
% |" f- C4 n/ P5 D$ UUPDATE `articles`
" s8 I" h7 M0 f6 h- \- L SET sum_votes =
: a0 K# a0 W6 @( F2 x: C WHERE `id` = 1;
$ n, O1 k4 ?; C1 f) v! c第一种方法看起来更干净,但是 在整个SELECT查询运行期间,表是否将被锁定?: ?0 H1 v4 G! ^. O2 I2 T, \5 k
7 H2 C; i3 f7 D m' p+ s
解决方案:
& f" f% j( p% K( f * V' d* Y' f$ Y& c
|' o" E. Y1 C2 e7 M
" V# u4 }. W3 B1 H% i/ x6 j, t
关于并发问题,您有一种 “简便”的 方法来防止第二种方法中的任何并发问题,在事务内部,在商品行上执行选择(For
6 a# R$ R' p/ P2 |, C0 R1 ^7 Bupdate现在是隐式的)。同一篇文章上的任何并发插入将无法获得此相同的锁,并且将等待您。' R. Z% S. I/ Q# ^$ P' i
使用新的默认隔离级别,甚至在事务中甚至不使用序列化级别,您都不会在投票表上看到任何并发插入,直到事务结束。因此,您的SUM应该保持连贯性7 k+ j: r- {! W# c, b3 D
或看起来像连贯性- o8 Q( U$ k3 F8 Q
。但是,如果并发事务在同一文章上插入一个投票并在您之前提交(而第二个事务看不到您的插入),则最后一次提交的事务将覆盖计数器,您将失去1票。. y3 M) n; R; @
因此,请使用之前的select方法对文章进行行锁定
4 U1 W8 l% L* B* s$ p: q7 o(当然,并在事务中完成您的工作)。它很容易测试,可以在MySQL上打开2个交互式会话,并使用BEGIN开始交易。8 X) h7 A1 J! U# l9 O
如果使用触发器,则默认情况下您处于事务中。但是我认为您也应该在商品表上执行选择,以为运行中的并发触发器创建隐式行锁(难以测试)。# d" h. j& ?3 n& d7 z
不要忘记删除触发器。
# g4 ]+ `- n8 Y7 b8 L( D p不要忘记更新触发器。' }& ^/ G" m$ O; W# D; Z# g
如果您不使用触发器而留在代码中,则在进行交易之前,请小心对投票的每个插入/删除/更新查询都应在相应的文章上执行行锁定。忘记一个不是很困难。' l( m; s. ^& @$ E, s F+ }4 y
- t" `3 l8 S1 [. y
最后一点:在开始使用交易之前,进行更困难的交易:. Z6 R/ Q* k5 `
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
- j7 ~5 r& n9 y+ F$ b这样,您不需要对文章进行行锁定,MySQL将检测到同一行上可能发生的写入,并会阻塞其他事务,直到您完成操作为止。9 ^* W/ I' r4 ?) Y/ K/ N# A
但是,请勿使用您根据上一个请求计算出的内容
\5 i- G# `; ^。更新查询将等待商品的锁释放,当第一个事务释放锁时COMMIT,SUM应该再次进行计算。因此,更新查询应包含SUM或进行添加。* `: H q4 [5 g8 K
update articles set nb_votes=(SELECT count(*) from vote) where id=2;% J a; z0 P7 _3 Y$ e! J
在这里,您会看到MySQL很聪明,如果在同时执行插入操作的同时有2个事务试图执行此操作,则会检测到死锁。在序列化级别中,我还没有找到一种使用以下方法获得错误值的方法:
$ {) v" n, r: J* X SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
6 ?( ^+ F( j/ u4 C* _" Y BEGIN;/ y3 m/ g% i5 O$ A3 j
insert into vote (...
4 U0 C; [1 P- `" A7 X& X' Z update articles set nb_votes=(
$ d7 h) F( ` N0 Q" E9 }; k8 m. F SELECT count(*) from vote where article_id=xx+ j. A3 Q4 Z- T+ F0 s/ Y
) where id=XX;% \* H# U9 Z8 A: `% w' v1 X8 y1 \
COMMIT;( V2 F. _/ _. A# Y
但是请准备好处理您必须重做的突破性交易。 |
|
|
|
|
|