10个行锁、死锁案例⭐️24张加锁剖析图彻底搞懂Innodb行锁加锁规定!
上篇文章 咱们形容原子性与隔离性的实现,其中形容读操作解决隔离性问题的计划时还遗留了一个问题:写操作是如何解决不同的隔离性问题?
本篇文章将会解决这个问题并形容MySQL中的锁、总结Innodb中行锁加锁规定、列举办锁、死锁案例剖析等
再浏览本篇文章前,至多要了解查问应用索引的流程、mvcc等常识(不了解的同学能够依据专栏程序进行浏览)
MySQL锁的分类
从锁的作用域上划分:全局锁、表锁、页锁、行锁
- 全局锁:锁整个数据库实例,罕用数据备份,禁止全局写,只容许读
表锁:锁表,对表进行加锁
- 元数据锁:表构造批改时
- 表X锁:表独占锁
- 表S锁:表共享锁
- 页锁:位于表锁与行锁两头作用域的锁
- 行锁(innodb特有):锁记录,其中蕴含独占锁(写锁,X锁)和共享锁(读锁,S锁)
从性能上划分:意向锁、插入意向锁、自增长锁、乐观锁、乐观锁...
意向锁:表锁,示意无意向往表中加锁,获取行锁前会加意向锁,当须要加表锁时,要通过意向锁判断表中是否有行锁
- 独占意向锁 IX:意向往表中加X锁,兼容IX、IS,不兼容X、S(表级别)
- 共享意向锁 IS:意向往表中加S锁,兼容IX、IS、S,不兼容X(表级别)
- 插入意向锁:隐式锁,意向往表中插入记录
- 自增长锁:隐式锁,在并发场景下不确定插入数量会应用自增长锁加锁生成自增值,如果确定则应用互斥锁(间断模式)
- 乐观锁:乐观锁能够用加行锁实现
- 乐观锁:乐观锁能够用版本号判断实现
这些锁有些是MySQL提供的,有些是存储引擎提供的,比方Innodb反对的行锁粒度小,并发性能高,不易抵触
但在某些场景上行锁还是会发生冲突(阻塞),因而咱们须要深刻把握行锁的加锁规定能力在遇到这种场景时剖析出问题
Innodb的行锁加锁规定
后面说到行锁分为独占锁 X锁和共享锁 S锁,行锁除了会应用这种模型外,还会应用到一些其余的模型
锁模型
record:记录锁,用于锁定某条记录 模型应用S/X (也就是独占锁、共享锁)
gap:间隙锁,用于锁定某两条记录之间,禁止插入,用于避免幻读 模型应用GAP
next key:临键锁,相当于记录锁 + 间隙锁 模型应用X/S,GAP
在lock mode中罕用X或S代表独占/共享的record,GAP则不分X或S
如果是独占的临键锁则是X、GAP,共享的临键锁则是S、GAP
(这个lock mode前面案例时会用到,示意以后SQL被哪种锁模型阻塞)
不同隔离级别下的加锁
加锁的目标就是为了可能满足事务隔离性从而达到数据的一致性
在不同隔离级别、应用不同类型SQL(增删改查)、走不同索引(主键和非主键、惟一和非惟一)、查问条件(等值查问、范畴查问)、MySQL版本 等诸多因素都会导致加锁的细节发生变化
因而只须要大抵把握加锁规定,并联合产生阻塞的记录排查出产生阻塞的问题,再进行优化、解决即可(本文基于5.7.X)
在RU(Read Uncommitted)下,读不加锁,写应用X record,因为写应用X record 则不会产生脏写,会产生脏读、不可反复读、幻读,
在RC(Read Committed)下,读应用mvcc(每次读生成read view),写应用X record,不会产生脏写、脏读,但会有不可反复读和幻读
在RR(Repeatable Read)下,读应用mvcc(第一次读生成read view),写应用X next key,不会产生脏写、脏读、不可反复读和大部分幻读,极其场景的幻读会产生
在S(Serializable)下,主动提交状况下读应用mvcc,手动提交下读应用S next key,写应用X next key,不会产生脏写、脏读、不可反复读、幻读
在罕用的隔离级别RC、RR中,读都是应用mvcc机制(不加锁)来进步并发性能的
锁定读的加锁
在S串行化下,读会加S锁,那select如何加锁呢?
S锁:select ... lock in share mode
X锁:select ... for update
通常把加锁的select称为锁定读,而在一般的update和delete时,须要先进行读(找到记录)再操作,在这种状况下加锁规定也能够归为锁定读
update与delete是写操作,必定是加X锁的
(以下锁定读和新增的加锁规定是总结,搭配案例查看,一开始看不懂不要紧~)
锁定读加锁规定
在RC及以下隔离级别,锁定读应用record锁;在RR及以上隔离级别,锁定读应用next key锁 (间隙锁的范畴是前开后闭,案例详细描述)
(具体S、X锁则看SQL,如果是
select ... lock in share mode
则是S锁,如果是select ... for update
、update ...
、delete ...
则是X锁)- 等值查问:如果找不到记录,该查问条件所在区间加GAP锁;如果找到记录,惟一索引临键锁进化为记录锁,非惟一索引须要扫描到第一条不满足条件的记录,最初临键锁进化为间隙锁(不在最初一条不满足条件的记录上加记录锁)
- 范畴查问:非惟一索引须要扫描到第一条不满足条件的记录(5.7中惟一索引也会扫描第一条不满足条件的记录8.0修复,后文形容)
在查找的过程中,应用到什么索引就在那个索引上加锁,遍历到哪条记录就给哪条先加锁
(查找时走二级索引,如果要回表查聚簇索引,则还会在聚簇索引上加锁)
(批改时如果二级索引上也存在要批改的值,则还要去二级索引中查找加锁并批改)
- 在RC及以下隔离级别下,查找过程中如果记录不满足以后查问条件则会开释锁;在RR及以上无论是否满足查问条件,只有遍历过记录就会加锁,直到事务提交才开释(RR及以上获取锁的工夫会更长)
新增的加锁
后面说到update、delete这种先查再写的操作能够看成加X锁的锁定读,而select的锁定读分为S、X,还剩insert的规定没有阐明
新减少锁规定
新减少锁规定分为三种状况:失常状况、遇到反复抵触的状况、外键状况
新增时加的锁叫插入意向锁,它是隐式锁
当别的事务想要获取该记录的X/S锁时,查看该记录的事务id是不是沉闷事务,如果沉闷(事务未提交)则会帮新增记录的事务生成锁构造,此时插入意向锁变成显示锁(能够看成X锁)
失常状况下加锁:
- 个别状况下,插入应用隐式锁(插入意向锁),不生成锁构造
- 当插入意向锁(隐式锁)被其余事务生成锁构造时变为显示锁(X record)
反复抵触加锁:
- 当insert遇到反复主键抵触时,RC及以下加S record,RR及以上加S next key
当insert遇到反复惟一二级索引时,加S next key
如果应用
ON DUPLICATE KEY update
那么S锁会换成X锁
外键加锁:个别不做物理外键,略...
行锁案例剖析
搭建环境
先建设一张测试表,其中id为主键,以s\_name建设索引
CREATE TABLE `s` ( `id` int(11) NOT NULL, `s_name` varchar(255) DEFAULT NULL, `s_age` varchar(255) DEFAULT NULL, PRIMARY KEY (`id`), KEY `name_idx` (`s_name`)) ENGINE=InnoDB DEFAULT CHARSET=utf8;
再插入一些记录
INSERT INTO `s` (`id`, `s_name`, `s_age`) VALUES (1, 'juejin', '1');INSERT INTO `s` (`id`, `s_name`, `s_age`) VALUES (10, 'nb', '10');INSERT INTO `s` (`id`, `s_name`, `s_age`) VALUES (20, 'caicai菜菜', '20');INSERT INTO `s` (`id`, `s_name`, `s_age`) VALUES (25, 'ai', '25');
聚簇索引和s\_name索引的存储图像简化成如下:
后面说过GAP须要加在记录之间,如果是第一条记录或者最初一条记录要避免插入,该如何加GAP锁呢?
Infimum和Supremum的呈现就可能解决这种问题,它们用于标识每页的最小值和最大值
留神:因为RC、RR是罕用的隔离级别,案例也是应用这两种隔离级别进行阐明
分析方法
能够通过零碎库查看行锁阻塞的相干信息
5.7 阻塞的锁、事务等信息在information\_schema库中的innodb\_locks、innodb\_lock\_waits、innodb\_trx等表
8.0 的相干信息则是在performance\_schema库中
lock记录信息简介
lock\_id 锁id 由事务id、存储信息组成 会变动
lock\_trx\_id 事务ID 42388为先开启的事务 (事务ID全局自增)
lock\_mode 阻塞的锁为X锁,还有其余模式:S、X、IS、IX、GAP、AUTO\_INC等
lock\_type 锁类型为行锁record ,还有表锁:table
lock\_table 锁的表 ; lock\_index 锁的索引 (二级索引)
lock\_space 、page 、rec 锁的表空间id、页、堆号等存储信息
lock\_data 示意锁的数据,个别是行记录 'caicai菜菜',20 (s\_name,id)
还能够通过联表查问获取行锁阻塞的信息
SELECT r.trx_id waiting_trx_id, r.trx_mysql_thread_id waiting_thread, r.trx_query waiting_query, rl.lock_mode waiting_lock_mode, rl.lock_type waiting_lock_type, b.trx_id blocking_trx_id, b.trx_mysql_thread_id blocking_thread, b.trx_query blocking_query, bl.lock_mode blocking_lock_mode, bl.lock_type blocking_lock_typeFROM information_schema.innodb_lock_waits wINNER JOIN information_schema.innodb_trx b ON b.trx_id = w.blocking_trx_idINNER JOIN information_schema.innodb_trx r ON r.trx_id = w.requesting_trx_idinner join information_schema.innodb_locks rl on r.trx_id = rl.lock_trx_idinner join information_schema.innodb_locks bl on b.trx_id = bl.lock_trx_id;
又或者通过 innodb的日志 (show engine innodb status)查看阻塞信息...
(后文剖析再说)
案例:RC、RR下的加锁
T1 | T2 | |
---|---|---|
1 | begin;<br/>select * from s where id\>=10 and id<=20 for update; | |
2 | insert into s values (12,'caicaiJava',12);<br/>(阻塞) | |
3 | commit; |
T1事务在10,20之间会加GAP锁,因而T2新增时会被阻塞
设置为RC后不再阻塞,因为RC下不加GAP锁不避免插入
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;SELECT @@tx_isolation;
但如果是要获取记录锁则还是会被阻塞 (批改id为10的记录 update s set s_name = '666' where id = 10
)
依据该案例能够阐明规定一:RC及以下应用记录锁、RR及以上应用临键锁
案例:等值查问
等值查问:匹配不到满足条件的记录
T1 | T2 | T3 | |
---|---|---|---|
1 | begin;<br/>select * from s where id=15 for update; | ||
2 | insert into s values (11,'caicaiJava11',11);<br/>(阻塞) | ||
3 | insert into s values (19,'caicaiJava11',19);<br/>(阻塞) | ||
4 | commit; |
通过阻塞记录能够看到T2,T3事务被主键索引上数据为20的临键锁(的GAP)阻塞
等值查问如果匹配不到值会在该区间加GAP锁
图中向下黑箭头为GAP锁
例如T1等值查问id=15,没有id=15的记录则会加锁在15这个区间加GAP锁
等值查问:匹配到满足条件的记录
T1 | T2 | T3 | |
---|---|---|---|
1 | begin;<br/>select * from s where id=20 for update; | ||
2 | insert into s values (15,'菜菜的后端私房菜',15);<br/>(不阻塞) | ||
3 | update s set s\_name = '菜菜的后端私房菜' where id = 20;<br/>(阻塞) | ||
4 | commit; |
因为惟一索引上雷同的记录只有一条,当等值查问匹配时,临键锁会进化成记录锁,因而T2不被阻塞 T3被阻塞
图中为T3被数据为20上的X锁阻塞
惟一索引等值查问间隙锁进化为记录锁
(图中蓝色为记录锁)
非惟一索引等值查问
T1 | T2 | T3 | |
---|---|---|---|
1 | begin;<br/>select s\_name,id from s where s\_name='caicai菜菜' for update; | ||
2 | insert into s values (15,'bilibili',15);<br/>(阻塞) | ||
3 | insert into s values (18,'da',18);<br/>(阻塞) | ||
4 | commit; |
为了确保 select s_name,id from s where s_name='caicai菜菜' for update
应用s\_name索引,我将查问列换成s\_name上存在的列,防止回表(确保应用s\_name)
- 先定位到
s_name='caicai菜菜'
的记录,加锁:(ai,caicai菜菜] - 因为不确定满足
s_name='caicai菜菜'
的记录是否有反复,于是持续后查问,加锁:(caicai菜菜,juejin] - 因为juejin不满足查问条件,于是进化为间隙锁,加锁:(caicai菜菜,juejin)
最终加锁范畴 = (ai,caicai菜菜] + (caicai菜菜,juejin) = (ai,juejin)
(留神:我这里的加锁范畴是简化的,没有带上主键信息;残缺信息如下图lock\_data中的juejin,1)
而后再来剖析T2,T3的插入语句,首先它们须要在聚簇索引和name\_idx索引上新增数据,因为聚簇索引未加锁,因而不影响插入
然而name\_idx索引上存在锁,T2事务 bilibili 会插入到ai和caicai菜菜记录之间,T3事务会插入到caicai菜菜和juejin这两条记录间,因而被GAP锁阻塞
通过阻塞记录也能够看出T2,T3均被临键锁阻塞
至此等值查问的案例剖析结束,小结如下:
- 等值查问找不到记录:该区间加GAP锁
等值查问找到记录
- 惟一索引:临键锁会进化为记录锁
- 非惟一索引:始终扫描到第一条不满足条件的记录并将临键锁进化为间隙锁
案例:范畴查问
在下面等值查问 + 非惟一索引的场景下,因为无奈判断该值数量,因而会始终扫描,能够把这种场景了解成范畴查问
T1 | T2 | |
---|---|---|
1 | begin;<br/>select * from s where id\>=10 and id<=20 for update; | |
2 | insert into s values (21,'caicaiJava',21);<br/>(阻塞) | |
3 | commit; |
依照失常思路来说,我的查问条件在10-20,那么就不能往这个范畴外再加锁了
然而新增该范畴外的记录是会阻塞的(我明明查问条件在10\~20,后果超过20你也给我加锁是吧?)
咱们来剖析下T1加锁过程: id>=10 and id<=20
- 定位第一条记录(id=10),按情理加间隙锁(前开后闭)应该是(1,10],然而有等值查问的优化,间隙锁进化为记录锁,因而只对10加锁 [10]
- 持续向后范畴扫描,定位到记录(id=20),加锁范畴(10,20]
- 依照失常思路主键是惟一的,我曾经找到一条20了,那我应该退出才对呀,然而它还是会持续扫描,直到第一条不满足查问条件的值(id=25)并将临键锁锁进化成间隙锁,也就是不在25加记录锁,因而加锁范畴(20,25)
最终加锁范畴 [10] + (10,20] + (20,25) = [10,25),因而插入主键为21时会被阻塞
思考:依照失常的思路,当在非惟一索引上时,这么扫描没问题,因为不晓得满足后果的20有多少条,只能往后扫描找到第一条不满足条件的记录;而在惟一索引上找到最初一个满足条件的记录20后,还持续往后加锁是不是有点奇怪呢?
我在8.0的版本中重现这个操作,插入id=21不再被阻塞,应该是在惟一索引上扫描到最终满足条件的记录(id=20)就完结,加锁范畴如下图(在5.7中这应该算bug)
范畴查问时无论是否惟一索引都会扫描到第一条不满足条件的记录,而后临键锁进化为间隙锁 (8.0修复惟一索引范畴查问时的bug)
案例:查找过程中怎么加锁
T1 | T2 | |
---|---|---|
1 | begin;<br/>update s set s\_name = 'caicai菜菜' where id = 20; | |
2 | select s\_name,id from s where s\_name like 'cai%' for update; <br/>(阻塞) | |
3 | commit; |
T1 事务在批改时先应用聚簇索引定位到id=20的记录,批改后通过主键id=20找到二级索引上的记录进行批改,因而聚簇索引、二级索引上都会获取锁
T2 事务锁定读二级索引时,因为查问条件满足二级索引的值,因而不须要回表,但因为T1事务锁住二级索引上的记录,因而产生阻塞
在该案例中阐明:加锁时应用什么索引就要在那个索引上加锁,遍历到哪些记录就要在哪些记录上加锁
delete:与主键相干的二级索引必定也要删除,因而二级索引上对应主键值的记录也会被加锁
update:如果在二级索引上批改,那么肯定回去聚簇索引上批改,因而聚簇索引也会被加锁;如果在聚簇索引上批改,二级索引可能会须要被加锁(如上案例,如果批改的是s\_age那么二级索引就不须要加锁)
select:应用什么索引就在什么索引上加锁,比方应用聚簇索引就要在聚簇索引上加锁,应用二级索引就在二级索引上加锁(如果要回表也要在聚簇索引上加锁)
案例:RC、RR什么时候开释锁
RC及以下,RR及以上在获取完锁后,开释锁的机会也不同
RR下
T1 | T2 | T3 | |
---|---|---|---|
1 | begin;<br/>update s force index (name\_idx) set s\_age = 20 where s\_name \> 'c' and s\_age \> 18; | ||
2 | select * from s where id = 1 for update;<br/>(阻塞) | insert into s values (33,'zz',33);<br/>(阻塞) | |
3 | commit; |
T3插入的记录满足 s_name > 'c' and s_age > 18
的记录被阻塞情有可原
那为啥T2 id=1不满足 s_name > 'c' and s_age > 18
也被阻塞了呢?
T1事务是一条批改语句,我应用force index 让它强制应用name\_idx索引,查问条件为 s_name > 'c' and s_age > 18
因为name\_idx上不存在s\_age,须要判断s\_age就要去聚簇索引,因而聚簇索引上也会被加锁
T1在name\_idx上,依据查问条件s\_name > 'c'进行加锁
- 定位第一条s\_name大于c的记录,加锁(ai,caicai菜菜]
- 依据主键值id=20去聚簇索引中找到该记录,加锁[20,20]
- 查看是否满足s\_age>18的条件,如果满足则进行批改(不满足不会开释锁)
- 持续循环,回到name\_idx上寻找下一条记录(直到不满足查问条件的记录或遍历完记录则退出)
依据1-3的步骤,会在索引上这样加锁
最终加锁状态:(name\_id中的+∞则指的是supremum)
其中只有id=20的记录满足 s_name > 'c' and s_age > 18
,即便这些记录不满足条件也不会开释锁
因而T2要获取聚簇索引id=1的记录时被阻塞,而T3则是被supremum阻塞
在RR下应用的索引遍历到哪就把锁加到哪,即便不满足查问条件也不会开释锁,直到事务提交才开释
RC
设置隔离级别
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;SELECT @@tx_isolation;
T1 | T2 | T3 | |
---|---|---|---|
1 | begin;<br/>update s force index (name\_idx) set s\_age = 20 where s\_name \> 'c' and s\_age \> 18; | ||
2 | select * from s where id = 1 for update;<br/>(不阻塞) | select * from s where id = 20 for update;<br/>(阻塞) | |
3 | commit; |
遍历流程与RR状况类似,不同的是RC只加记录锁,并且不满足条件的记录会立刻开释锁,因而T2不被阻塞,满足条件的T3被阻塞
加锁如下图
遍历到哪条记录就先加锁,然而RC对于不满足查问条件的记录会开释锁
死锁案例剖析
死锁案例剖析的是insert加的锁,配合下面新减少锁规定查看
案例:新增死锁
先将name\_idx改为惟一索引
T1 | T2 | |
---|---|---|
1 | begin;<br/>insert into s values (5,'bilibili',5); | |
2 | insert into s values (7,'bilibili',7); <br/>(阻塞) | |
3 | insert into s values (6,'balibali',6); | |
4 | 死锁 回滚 |
T1插入bilibili,T2也插入bilibili,依照情理应该报错惟一键反复呀,T2怎么阻塞了呢?
T1后续再插入balibali居然产生死锁了!啥状况呀?同学们能够先依据后面说到的insert加锁规定,大胆猜想喔\~
查看最近的死锁日志
须要留神的是innodb lock表中锁相干信息记录只有正在产生时才存在,像这种产生死锁,回滚事务后是看不到的,因而咱们来看看死锁日志
show engine innodb status 查看innodb状态,其中有一段最近检测到的死锁 latest detected deadlock
红色框示意事务和持有/期待的锁
绿色框示意锁的信息(都是同一把X锁)
如果日志还是看不太懂的话,来看看上面这段剖析吧(次要说name\_idx索引上的流程哈)
1、T1插入bilibili(隐式锁)
2、T2插入bilibili发生冲突,T2帮T1生成锁构造(隐式锁转化为显示锁,T1取得X record),T2要加S临键锁,先获取GAP锁(胜利),再获取S锁(被T1的X record阻塞)
T1事务id为42861,T2事务id为42867:依据锁信息能够看到T2想加的S锁被T1的X锁阻塞
3、T1插入balibali,插入意向锁被T2的GAP锁阻塞(死锁成环:T1期待T2的GAP,T2期待T1的X)
图中蓝色与T1无关,彩色与T2无关
T1:持有[bilibili,bilibili] X锁,要插入balabala被T2的间隙锁(ai,bilibili)阻塞
T2:持有间隙锁(ai,bilibili),要插入[bilibili,bilibili]S锁被T1的[bilibili,bilibili]X锁阻塞
那么如何解决死锁呢?
先来看看死锁产生的四个条件:互斥、占有资源不放、占有资源持续申请资源、期待资源成环
MySQL通过回滚事务的形式解决死锁,也就是解决占有资源不放
但MySQL死锁检测是十分消耗CPU的,为了防止死锁检测,咱们应该在业务层面避免死锁产生
首先互斥、占有资源不放两个条件是无奈毁坏的,因为加锁由MySQL来实现
而毁坏占有资源持续申请资源的代价可能会很大,比方:让业务层加锁解决
性价比最高的应该是毁坏期待资源成环,当产生死锁时,通过剖析日志、加锁规定,调整业务代码获取资源的程序防止产生死锁
案例:雷同的新增产生死锁
T1 | T2 | T3 | |
---|---|---|---|
1 | insert into s values (15,'bili',15); | ||
2 | insert into s values (15,'bili',15);<br/>(阻塞) | insert into s values (15,'bili',15);<br/>(阻塞) | |
3 | rollback; | ||
4 | 死锁 |
T1、T2、T3新增雷同的记录
T1新增后,T2、T3 会帮T1生成锁构造X锁从而被阻塞
当T1回滚时,T2,T3居然产生死锁?
剖析流程
- T1 插入 加隐式锁
- T2 插入雷同惟一记录,帮T1生成X锁,本人获取S next key,先获取gap(胜利),再获取S record(此时被T1的X锁阻塞);T3 与 T2 类似,获取到gap 再获取S record 时被T1的X阻塞
- T1 回滚,T2、T3获取S record胜利,此时它们都还要获取X record(插入意向锁转化为显示锁X)导致死锁成环(T2要加X锁被T3的GAP阻塞,T3要加X锁被T2的GAP阻塞)
图中T2、T3都对bili加S next key锁(橙色记录和后面的彩色间隙),当它们都想加插入动向X锁(蓝色记录),同时也被各自的GAP锁阻塞
查看死锁日志
总结
本篇文章通过大量案例、图例剖析不同状况下的行锁加锁规定
update、delete 先查再改,能够看成锁定读,insert则是有独自一套加锁规定
锁定读加锁规定
在RC及以下隔离级别,锁定读应用record锁;在RR及以上隔离级别,锁定读应用next key锁
等值查问:如果找不到记录,该查问条件所在区间加GAP锁;如果找到记录,惟一索引临键锁进化为记录锁,非惟一索引须要扫描到第一条不满足条件的记录,最初临键锁进化为间隙锁(不在最初一条不满足条件的记录上加记录锁)
范畴查问:非惟一索引须要扫描到第一条不满足条件的记录(5.7中惟一索引也会扫描第一条不满足条件的记录8.0修复,后文形容)
在查找的过程中,应用到什么索引就在那个索引上加锁,遍历到哪条记录就给哪条先加锁
在RC及以下隔离级别下,查找过程中如果记录不满足以后查问条件则会开释锁;在RR及以上无论是否满足查问条件,只有遍历过记录就会加锁,直到事务提交才开释
insert加锁规定
失常状况下加锁:
- 个别状况下,插入应用隐式锁(插入意向锁),不生成锁构造
- 当插入意向锁(隐式锁)被其余事务生成锁构造时变为显示锁(X record)
反复抵触加锁:
- 当insert遇到反复主键抵触时,RC及以下加S record,RR及以上加S next key
- 当insert遇到反复惟一二级索引时,加S next key
如果应用ON DUPLICATE KEY update
那么S锁会换成X锁
外键加锁:个别不做物理外键,略...
最初(不要白嫖,一键三连求求拉\~)
本篇文章被支出专栏 MySQL进阶之路,感兴趣的同学能够继续关注喔
本篇文章笔记以及案例被支出 gitee-StudyJava、 github-StudyJava 感兴趣的同学能够stat下继续关注喔\~
有什么问题能够在评论区交换,如果感觉菜菜写的不错,能够点赞、关注、珍藏反对一下\~
关注菜菜,分享更多干货,公众号:菜菜的后端私房菜
本文由博客一文多发平台 OpenWrite 公布!