处理ON INSERT触发器时如何锁定innodb表?
技术问答
296 人阅读
|
0 人回复
|
2023-09-14
|
我有两个innodb表:* c ]( b" e: j
文章
$ P& d- p1 U. A6 Z q0 i0 F A- Bid | title | sum_votes+ L) n7 G; n, B# \
------------------------------
4 j" F' k) T: G- Z) `1 | art 1 | 5
q8 o/ L& [/ b: m5 O* C2 | art 2 | 8* d' j2 f/ u: o
3 | art 3 | 350 E, I" r& K5 `( F
票数
) V2 |: K) U# a* q% W% Hid | article_id | vote
8 Q5 j# {6 h2 W------------------------------. _; t; Z* F, z; G. c: F
1 | 1 | 1
# D, v. U, `' U( s; W; s6 L2 | 1 | 20 [! ~& I. s6 q n4 u- x9 f
3 | 1 | 2
1 r7 c1 P# v0 \8 k! D4 | 2 | 10
& E" E. K4 Q9 Q2 [% l) ?5 | 2 | -2
, K/ j1 e9 K2 e3 P' t. p6 k _1 d! W2 ^6 | 3 | 105 H, H* f, K. w% n) J6 U
7 | 3 | 15
! z4 A/ I+ ?7 J) \8 | 3 | 12
1 ?: M. P. ^/ ^# p! B# E/ s9 | 3 | -2
7 T! E) `0 k9 S- v/ h, g, m. b将新记录插入votes表中时,我想通过计算所有投票的总和来更新表中的sum_votes字段articles。
8 J8 X2 ?1 m: e. o问题& O" {% \; ]) R# D
如果SUM()计算本身非常votes繁琐(表有700K条记录),则哪种方法更有效。! I' h7 c" b4 F* K& M* E3 r
1.创建触发器, V, n; @) K7 c8 r" G
CREATE TRIGGER `views_on_insert`' ]' N2 w4 Z3 O
AFTER INSERT
7 J, A+ A# [' U7 }" Z" CON `votes`
& U: S, n" e% w( @8 ^FOR EACH ROW( m5 x" T2 [2 A6 S
BEGIN: R% T% Y6 w% {) N" T9 F
UPDATE `articles` SET4 K T2 C" ]1 k/ h
sum_votes = (
- o" G+ N2 N. D. a SELECT SUM(`vote`)
' g+ D9 L$ [. d3 z/ Y n FROM `votes`4 H6 E- ^8 F; ~9 n
WHERE `id` = NEW.article_id9 ?; V7 _3 ]* j0 ^: y& _; c
)
- x; H9 |. i7 V6 X4 C. q0 }8 ?" } WHERE `id` = NEW.article_id;
% k3 }3 ?. i8 ^ Q1 j8 A8 q0 y4 ~END;
( |/ b8 ~% H3 T6 M6 w8 P( P1 E2.在我的应用程序中使用两个查询+ l3 ~6 o9 Q m6 K
SELECT SUM(`vote`) FROM `votes` WHERE `article_id` = 1;. {* o3 X( n0 u/ |/ s
UPDATE `articles` ! T' v( v6 H0 F: P9 f m. h' k+ o
SET sum_votes =
) Q/ C& s* ^" K; {+ S4 e1 a) x WHERE `id` = 1;7 G# M9 N8 `- G9 {
第一种方法看起来更干净,但是 在整个SELECT查询运行期间,表是否将被锁定?& q4 _4 I. K7 L6 x( T5 L$ p6 p
) F1 I. z0 f3 d& U$ R
解决方案:% N8 i5 X5 @" ]) Z
: \6 o" S/ n' q6 M* F* F# G2 f& t
3 x$ S: f, C$ M
关于并发问题,您有一种 “简便”的 方法来防止第二种方法中的任何并发问题,在事务内部,在商品行上执行选择(For- @/ G& t$ B# A: l8 G6 n
update现在是隐式的)。同一篇文章上的任何并发插入将无法获得此相同的锁,并且将等待您。
/ \: w* z3 Z% L! A' P& G* F4 |4 Z使用新的默认隔离级别,甚至在事务中甚至不使用序列化级别,您都不会在投票表上看到任何并发插入,直到事务结束。因此,您的SUM应该保持连贯性
& }. S/ L3 W: _或看起来像连贯性3 [$ A! W$ q1 K1 ?# c+ G# B
。但是,如果并发事务在同一文章上插入一个投票并在您之前提交(而第二个事务看不到您的插入),则最后一次提交的事务将覆盖计数器,您将失去1票。! g5 W% d+ B0 ^# y$ Z# k3 p6 N
因此,请使用之前的select方法对文章进行行锁定) U6 y6 p5 J$ Y J+ d& h$ [ [
(当然,并在事务中完成您的工作)。它很容易测试,可以在MySQL上打开2个交互式会话,并使用BEGIN开始交易。4 B A$ g+ B7 p* @. }
如果使用触发器,则默认情况下您处于事务中。但是我认为您也应该在商品表上执行选择,以为运行中的并发触发器创建隐式行锁(难以测试)。5 m6 k# a+ P' f/ S% c3 L
不要忘记删除触发器。
2 i1 M- W% e; ^) Z+ D2 u& f3 Q$ k" ~; |不要忘记更新触发器。
) @/ t0 q9 q& P6 i. T$ Y+ {如果您不使用触发器而留在代码中,则在进行交易之前,请小心对投票的每个插入/删除/更新查询都应在相应的文章上执行行锁定。忘记一个不是很困难。
7 o/ b: t0 E# }, L4 V. F! d( l. r& H0 y0 P. M, I& n1 t
最后一点:在开始使用交易之前,进行更困难的交易:
/ \5 D$ _' ]0 FSET TRANSACTION ISOLATION LEVEL SERIALIZABLE;, |; s% @; R; C' D1 v2 A/ D
这样,您不需要对文章进行行锁定,MySQL将检测到同一行上可能发生的写入,并会阻塞其他事务,直到您完成操作为止。6 B) f! V; h" [ ~# H9 m" a4 T2 m% K& w
但是,请勿使用您根据上一个请求计算出的内容% X: ?" Z# t6 J9 i" P
。更新查询将等待商品的锁释放,当第一个事务释放锁时COMMIT,SUM应该再次进行计算。因此,更新查询应包含SUM或进行添加。
8 c# o/ j$ O3 dupdate articles set nb_votes=(SELECT count(*) from vote) where id=2;; k T7 I- [' p" u
在这里,您会看到MySQL很聪明,如果在同时执行插入操作的同时有2个事务试图执行此操作,则会检测到死锁。在序列化级别中,我还没有找到一种使用以下方法获得错误值的方法:7 K% ~- j3 r$ ]) ~# M
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
% {6 C9 s# L( z- U/ N, D* p7 ~0 C BEGIN;
+ [, ~( S$ D; e insert into vote (...6 h! h. T V+ t9 z& N
update articles set nb_votes=(
3 G4 u9 ^9 d5 I4 d, Q SELECT count(*) from vote where article_id=xx
$ Z! |0 X0 c+ c w) C( d$ o ) where id=XX;
9 I4 M4 x: _( ]. m9 Z6 h- E COMMIT;2 B, W; W# E$ K+ _8 n% t$ w/ K3 a
但是请准备好处理您必须重做的突破性交易。 |
|
|
|
|
|