4.技术-mysql(四)-锁-何时加锁?加什么锁?

一.背景

    通过上节分析锁的基本概念,我们知道锁的模式有:

行级别的:(排他行锁)X、(共享行锁)S。

在加行锁之前需要先对库表页加意向锁:(意向排他锁)IX、(意向共享锁)IS。

以及实现锁的三种算法:

1>.record lock:锁住当前记录

2>.gap lock:锁住记录中间

3>.next-key lock:record lock+gap lock

这一节我们详细的分析这些锁的使用场景。

二.整体使用场景

1.普通的select操作(一致性非锁定读)不加任何锁(Serializable隔离级别加锁)。

2.UPDATE、DELETE和INSERT以及SELECT ... FOR UPDATE语句加IX和X锁。

3.SELECT * FROM table_name WHERE ... LOCK IN SHARE MODE、

 唯一key Duplicate后当前事务会对该key加S锁。

create table new_table select * from old_talbe; 会对old_talbe加S锁

三.详细的分析加锁情况

注:不同的隔离级别解决的问题不同,比如读已提交解决脏读,重复读解决幻读,所以不同的隔离级别加锁机制不同(这点很重要,很多人上来看sql就说加什么锁,这种很不负责任)。

下图是一张整体的加锁这里只讨论Read Commited和Repeatable Read,其他两种基本不会用讨论意义不大。

分析加锁,首先要知道下面几点:(不同的条件加锁机制也不相同)

1>.隔离级别

2>.索引类型和执行计划

3>.更新的字段中是否有索引字段

先贴一下表结构

4.技术-mysql(四)-锁-何时加锁?加什么锁?_第1张图片

1>.场景一:Read Commited+无索引

设置隔离级别:set session transaction isolation level read uncommitted;

不走索引语句:update t_user set name ='mbj111' where text='text1'; 

图1:事务1模拟使用无索引字段更新未提交

4.技术-mysql(四)-锁-何时加锁?加什么锁?_第2张图片

图2:事务2模拟使用无索引字段更新

4.技术-mysql(四)-锁-何时加锁?加什么锁?_第3张图片

图3:确认下隔离级别:是Read Commited

4.技术-mysql(四)-锁-何时加锁?加什么锁?_第4张图片

图4:冲突锁展示,在主键索引加的X锁,id都是1。注:表字段解释见上篇。

4.技术-mysql(四)-锁-何时加锁?加什么锁?_第5张图片

结论:

Read Commited+无索引 对满足条件的记录对应的主键索引加X锁。

值得注意的是:此场景因为是全表扫描,实际处理过程较复杂如下:

4.技术-mysql(四)-锁-何时加锁?加什么锁?_第6张图片

1>.mysql server向innodb发起一笔当前读(一致性锁定读):select * from t_user where text='text1' 

2>.innodb收到该当前读请求发现全表扫描,会对所有主键索引的每一个节点加X锁并返回。

3>.mysql server收到结果集过滤,对不满足条件的记录解锁。

4>.mysql server对满足条件的记录发起更新的操作。

5>.innodb收到请求执行更新操作。

6>.mysql server发起commit命令给innodb

7>.innodb释放锁。

(更具体的流程后面的章节会详细的说)

从上面简易的流程可以看出,没有索引innodb对所有数据会执行加锁解锁的操作,这是由于MySQL的实现决定的如果一个条件无法通过索引快速过滤,那么存储引擎层面就会将所有记录加锁后返回,然后由MySQL Server层进行过滤。

在实际的实现中,MySQL有一些优化,在MySQL Server过滤条件,发现不满足后,会调用unlock_row方法,把不满足条件的记录放锁 (违背了2PL的约束,两阶段锁(Two-phase locking)约定加锁和解锁都在同一阶段执行,来保证加锁和解锁无交叉,在同一个事务中不会出现死锁)。这样做,保证了最后只会持有满足条件记录上的锁,但是每条记录的加锁操作还是不能省略的,并发下会影响性能。

2>.场景二:Read Commited+普通索引

普通索引语句:update t_user set name ='mbj123' where name='mbj111'; 

图1:数据展示

4.技术-mysql(四)-锁-何时加锁?加什么锁?_第7张图片

图2:执行计划展示

图3:RC隔离级别+模拟事务1根据普通索引更新未提交

4.技术-mysql(四)-锁-何时加锁?加什么锁?_第8张图片

图4:RC隔离级别+模拟事务2根据普通索引更新未提交

4.技术-mysql(四)-锁-何时加锁?加什么锁?_第9张图片

图5:冲突锁展示,可以看出对普通索引加了锁。

4.技术-mysql(四)-锁-何时加锁?加什么锁?_第10张图片

图6:事务2模式使用主键更新。

4.技术-mysql(四)-锁-何时加锁?加什么锁?_第11张图片

图7:冲突锁展示,可以看出对主键索引加了锁。

4.技术-mysql(四)-锁-何时加锁?加什么锁?_第12张图片

图8:模拟使用唯一索引更新(注意unique_no是字符型,值不带''不走索引)

4.技术-mysql(四)-锁-何时加锁?加什么锁?_第13张图片

图9:冲突锁展示,可以看出使用唯一索引更新依然对主键索引加了锁。

4.技术-mysql(四)-锁-何时加锁?加什么锁?_第14张图片

图10:模拟不使用索引。

4.技术-mysql(四)-锁-何时加锁?加什么锁?_第15张图片

图11:冲突锁展示,不使用索引意味着对主键加锁,引起主键锁冲突。

4.技术-mysql(四)-锁-何时加锁?加什么锁?_第16张图片

结论:RC+普通索引,会对所有满足条件的记录的普通索引加锁,并且会对对应的主键索引加锁。而对其他索引不加锁。

3>.场景三:Read Commited+唯一索引

图1:模拟使用唯一索引未提交

4.技术-mysql(四)-锁-何时加锁?加什么锁?_第17张图片

图2:模拟事务2使用唯一索引更新

4.技术-mysql(四)-锁-何时加锁?加什么锁?_第18张图片

图3:冲突锁展示,可以看出对唯一索引加锁

4.技术-mysql(四)-锁-何时加锁?加什么锁?_第19张图片

图4:事务2模拟主键更新

4.技术-mysql(四)-锁-何时加锁?加什么锁?_第20张图片

图5:冲突锁展示,可以看出引起主键锁冲突。

4.技术-mysql(四)-锁-何时加锁?加什么锁?_第21张图片

图6:事务2模拟使用非索引更新

4.技术-mysql(四)-锁-何时加锁?加什么锁?_第22张图片

图7:冲突锁展示:可以看出主键锁冲突了

4.技术-mysql(四)-锁-何时加锁?加什么锁?_第23张图片

图8:普通索引更新

4.技术-mysql(四)-锁-何时加锁?加什么锁?_第24张图片

图9:冲突锁展示:可以看出主键锁冲突了,因为普通所以会对主键加锁,而当前主键被事务1已经上锁。

4.技术-mysql(四)-锁-何时加锁?加什么锁?_第25张图片

结论:Read Commited+唯一索引:会对唯一索引和主键加锁,其他不加锁。

4>.场景四:Read Commited+主键索引

图1:事务1模拟主键事务未提交

4.技术-mysql(四)-锁-何时加锁?加什么锁?_第26张图片

图2:事务2模拟通过主键修改

4.技术-mysql(四)-锁-何时加锁?加什么锁?_第27张图片

图3:冲突锁展示:主键X锁冲突了

结论:Read Commited+主键索引,则对满足条件的主键索引加X锁。此场景性能最高。

5>.场景五:Repeatable Read+无索引

图1:事务1模拟无索引事务更新未提交

4.技术-mysql(四)-锁-何时加锁?加什么锁?_第28张图片

图2:事务2模拟无索引事务更新

4.技术-mysql(四)-锁-何时加锁?加什么锁?_第29张图片

图3:隔离级别展示,可以看出是可重复读

4.技术-mysql(四)-锁-何时加锁?加什么锁?_第30张图片

图4:锁展示,看出对主键加X锁了。

图5:事务2模拟更新其他行

4.技术-mysql(四)-锁-何时加锁?加什么锁?_第31张图片

图6:锁展示:等待id=1的主键X锁。

4.技术-mysql(四)-锁-何时加锁?加什么锁?_第32张图片

图7:事务2模拟执行插入

4.技术-mysql(四)-锁-何时加锁?加什么锁?_第33张图片

图8:锁展示:等待id=1的主键X锁。

4.技术-mysql(四)-锁-何时加锁?加什么锁?_第34张图片

结论:Repeatable Read+无索引,会对所有数据的主键加X锁,并且在记录的缝隙之间加GAP锁防止新记录插入来解决幻读。

也可以通过开启innodb_locks_unsafe_for_binlog来关闭GAP锁实现与RC隔离级别的处理方式。

mysql默认使用Repeatable Read隔离级别关闭innodb_locks_unsafe_for_binlog。

6>.场景六:Repeatable Read+普通索引

图0:看下目前的数据

4.技术-mysql(四)-锁-何时加锁?加什么锁?_第35张图片

 图1:事务1模拟根据普通索引修改未提交

4.技术-mysql(四)-锁-何时加锁?加什么锁?_第36张图片

图2:事务2根据普通索引修改同一行

4.技术-mysql(四)-锁-何时加锁?加什么锁?_第37张图片

图3:锁展示:锁定了普通索引。

4.技术-mysql(四)-锁-何时加锁?加什么锁?_第38张图片

图4:事务2根据id修改

4.技术-mysql(四)-锁-何时加锁?加什么锁?_第39张图片

图5:锁展示,锁定了主键

4.技术-mysql(四)-锁-何时加锁?加什么锁?_第40张图片

图6:事务2插入name=mbj14向(mbj12,mbj15]之间插入,被阻塞

4.技术-mysql(四)-锁-何时加锁?加什么锁?_第41张图片

图7:锁展示,可以看出等待锁模式为X+GAP。在普通索引上。

4.技术-mysql(四)-锁-何时加锁?加什么锁?_第42张图片

结论:对满足条件的记录加X锁+GAP锁,对应的主键加X锁。防止幻读。

注:上图的数据可以看出,普通索引mbj15上加X锁,在(mbj12,mbj15)和(mbj15,mbj18)加GAP锁,之间不会被插入数据。注意GAP锁是加在普通索引之间的不是唯一索引。

7>.场景七:Repeatable Read+唯一索引

图1:事务1根据唯一索引修改,未提交

4.技术-mysql(四)-锁-何时加锁?加什么锁?_第43张图片

图2:事务2根据唯一索引,修改同一行

4.技术-mysql(四)-锁-何时加锁?加什么锁?_第44张图片

图3:锁展示,对唯一索引加了X锁

4.技术-mysql(四)-锁-何时加锁?加什么锁?_第45张图片

图4:事务2根据其他字段修改,被阻塞

4.技术-mysql(四)-锁-何时加锁?加什么锁?_第46张图片

图5.锁展示,主键X锁。

4.技术-mysql(四)-锁-何时加锁?加什么锁?_第47张图片

图6.事务2插入name=mbj14向(mbj12,mbj15]之间插入,成功。

4.技术-mysql(四)-锁-何时加锁?加什么锁?_第48张图片

结论:Repeatable Read+唯一索引,同场景三,对满足条件的唯一索引和对应的主键索引+X锁。此时没有GAP锁,因为唯一性索引next key锁会降级为X锁。

8>.场景八:Repeatable Read+主键索引

图1:事务1模拟根据普通索引更新未提交

4.技术-mysql(四)-锁-何时加锁?加什么锁?_第49张图片

图2:更新其他行字段,发现更新成功,未阻塞。

4.技术-mysql(四)-锁-何时加锁?加什么锁?_第50张图片

图3:事务2模拟修改相同行,被阻塞

4.技术-mysql(四)-锁-何时加锁?加什么锁?_第51张图片

图4:锁冲突展示,id=15的主键索引加了X锁

4.技术-mysql(四)-锁-何时加锁?加什么锁?_第52张图片

图5:事务2模拟插入id=16的值,成功

4.技术-mysql(四)-锁-何时加锁?加什么锁?_第53张图片

结论:Repeatable Read+主键索引,同场景四,仅对主键加X锁。

场景九:update字段中含索引

图1:事务1模拟修改索引字段name,未提交。

4.技术-mysql(四)-锁-何时加锁?加什么锁?_第54张图片

图2:事务2根据name修改,对name索引上X锁。被阻塞

4.技术-mysql(四)-锁-何时加锁?加什么锁?_第55张图片

图3:锁展示,发现name索引冲突了。说明事务1正在占用。

4.技术-mysql(四)-锁-何时加锁?加什么锁?_第56张图片

结论:所有场景加锁的基础上,对索引字段修改会对该索引加锁。

场景十:S锁的使用场景之LOCK IN SHARE MODE

图1:事务1使用中

4.技术-mysql(四)-锁-何时加锁?加什么锁?_第57张图片

图2:select * from t_user LOCK IN SHARE MODE加S锁

4.技术-mysql(四)-锁-何时加锁?加什么锁?_第58张图片

图3:锁展示,可以看出加了S锁,与X锁冲突。

4.技术-mysql(四)-锁-何时加锁?加什么锁?_第59张图片

结论:S锁与X锁冲突。具体兼容性如下:

4.技术-mysql(四)-锁-何时加锁?加什么锁?_第60张图片

场景十一:S锁的使用场景之Duplicate entry for key 

图1:事务1发生Duplicate key

4.技术-mysql(四)-锁-何时加锁?加什么锁?_第61张图片

图2:事务2等待事务1

4.技术-mysql(四)-锁-何时加锁?加什么锁?_第62张图片

图3:锁冲突展示

4.技术-mysql(四)-锁-何时加锁?加什么锁?_第63张图片

结论:发生Duplicate key的事物会对该key持有S锁,直到回滚释放。此时对该索引加X锁是排斥的。

四.总结


    整理以上结论如下图,  后面章节会根据锁机制去编写适合高并发的SQL。

4.技术-mysql(四)-锁-何时加锁?加什么锁?_第64张图片

你可能感兴趣的:(4.技术-mysql(四)-锁-何时加锁?加什么锁?)