大家好,我是公众号:java小杰要加油 , 今天来分享一个关于mysql的知识点——mysql中的锁
事务并发访问情况 读-读 情况 并发事务读取相同的数据,并不会对数据造成影响,允许并发读 写-写 情况 多事务并发写写时会发生脏写 的情况,不过任何一个事务隔离级别都不允许此情况发生,通过加锁 来杜绝脏写 脏写
Q :能描述一下两个事务并发修改同一条数据时,mysql这个锁是怎么避免脏写的吗?
A :事务T1在更改这条数据前,就先内存中生成一把锁与此数据相关联(is_waiting为false,代表没有等待),然后咔咔一顿操作更改数据,这个时候,事务T2来了,发现此记录已经有一把锁与之相关联了(就是T1那一把锁),然后就开始等待(is_waiting为true代表正在等待),事务T1更改完数据提交事务后,就会把此事务对应的所结构释放掉,然后检测一下还有没有与此记录相关联的锁,结果发现T2还在苦苦的等待,就把T2的锁结构的(is_waiting为false,代表没有等待)然后把T2事务对应的线程唤醒,T2获取锁成功继续执行,总体流程如上。
读-写 /写-读 情况 在读-写 / 写 -读的情况下会出现脏读,不可重复读,幻读 的现象,不同的隔离级别可以避免不同的问题,具体相关内容可以看小杰的这篇文章 京东面试官问我:“聊聊MySql事务,MVCC?”
不过贴心的我还是列出来了 注:√代表可能发生,×代表不可能发生
读未提交(read uncommitted RU)
但是 RR在某些程度 上避免了幻读的发生
怎么避免脏读、不可重复读、幻读这些现象呢?其实有两种方案
mvcc里面最重要的莫过于ReadView了,它的存在保证了事务不可以读取到未提交的事务所作的更改,避免了脏读。 在RC隔离级别下,每次select读操作都会生成ReadView 在RR隔离级别下,只有第一次select读操作才会生成ReadView,之后的select读操作都复用这一个ReadView 方案二:读写操作都用加锁 某些业务场景不允许读取旧记录的值,每次读取都要读取最新的值。例如银行取款事务中,先把余额读取出来,再对余额进行操作。当这个事务在读取余额时,不允许其他事务对此余额进行访问读取,直到取款事务结束后才可以访问余额。所以在读数据的时候也要加锁
锁分类 当使用读写都加锁 这个方案来避免并发事务写-写 、读-写 、写-读 时而产生的脏读 ,不可重复读 ,幻读 现象时,那么这个锁它就要做到,读读时不相互影响,上面三种情况时要相互阻塞,这时锁也分了好几类,我们继续往下看
锁定读 共享锁(Shared Lock):简称S锁,在事务要读取 一条记录时,需要先获取该记录的S锁 独占锁(Exclusive Lock):简称X锁,也称排他锁,在事务要改动 一条记录时,需要先获取该记录的X锁 他们之间兼容关系如下 √代表可以兼容,×代表不可兼容
事务T1获取某记录的S锁后, 事务T2也可以 获取此记录的S锁 ,(兼容 ) 事务T2不可以 获取此记录的X锁 ,直到T1提交后将S锁 释放 (不兼容 ) 事务T1获取某记录的X锁后, 事务T2不可以 获取此记录的S锁 ,直到T1提交后将X锁 释放 (不兼容 ) 事务T2不可以 获取此记录的X锁 ,直到T1提交后将X锁 释放 (不兼容 ) 锁定读语句 SELECT .. LOCK IN SHARE MODE # 对读取的记录添加S锁
SELECT .. FOR UPDATE # 对读取的记录添加X锁
多粒度锁 前面提到的锁都是针对记录的,其实一个事务也可以在表级进行加锁(S锁、X锁)
T1给表加了S锁,那么 T2可以 继续获取此表的S锁 T2可以 继续获取此表中的某些记录的S锁 T2不可以 继续获取此表的X锁 T2不可以 继续获取此表中的某些记录的X锁 T1给表加了X锁,那么 T2不可以 继续获取此表的S锁 T2不可以 继续获取此表中的某些记录的S锁 T2不可以 继续获取此表的X锁 T2不可以 继续获取此表中的某些记录的X锁 可是怎么可能平白无故的就给表加锁呢,难道没什么条件吗?答案是肯定有条件的
若想给表加S锁 ,得先确保表中记录没有X锁 若想给表加X锁 ,得先确保表中记录没有X锁和S锁 但是这个怎么确保呢?难道要一行一行的遍历表中的所有数据吗?当然不是啦,聪明的大佬们想出了下面这两把锁
意向共享锁 (Intention Shared Lock):简称IS锁 ,当事务准备在某记录上加S锁时,需要先在表级别加上一个IS锁意向独占锁 (Intention Exclusive Lock):简称IX锁 ,当事务准备在某记录上加X锁时,需要先在表级别加上一个IX锁让我们来看下加上这两把锁之后的效果是什么样子的
当想给记录加S锁时,先给表加一个IS锁,然后再给记录加S锁 当想给记录加X锁时,先给表加IX锁,然后再给记录加X锁 然后 经过上面的操作之后
如果想给表加S锁,先看下表加没加IX锁,如果有的话,则表明此表中的记录有X锁,则需要等到IX锁释放掉后才可以加S锁 如果想给表加X锁,先看下表加没加IS锁或者IX锁,如果有的话,则表明此表中的记录有S锁或者X锁,则需要等到IS锁或者IX锁释放掉后才可以加X锁 这几种锁的兼容性如下表
IS、IX锁都是表级锁,他们可以共存。 他们的提出仅仅是为了在之后加表级别的S锁或者X锁时可以快速判断表中的记录是否被上锁,避免用遍历的方式来查看一行一行的去查看而已 InnoDB中的行级锁 Record Lock(记录锁) 官方名字 LOCK_REC_NOT_GAP 仅仅锁住一条记录 有S型和X型之分 Gap Lock(间隙锁) 官方名字 LOCK_GAP 给某记录加此锁后,阻塞数据在此记录和上一个记录的间隙插入,但是不锁定此记录 有S型和X型之分,可是并没有什么区别他们的作用是相同的,gap锁的作用仅仅是为了防止插入幻影记录而已 ,如果对一条记录加了gap锁(无论S/X型)并不会限制其他事务对这条记录加Record Lock或者Gap Lock Next-Key Lock(记录锁+间隙锁) 官方名字 LOCK_ORDINARY 既可以锁住某条记录,又可以组织其他事务在该记录面前插入新记录 Insert Intention Lock(插入意向锁锁) 官方名字 LOCK_INSERT_INTENTION 事务在插入记录时,如果插入的地方加了gap锁,那么此事务需要等待,此时此事务在等待时也需要生成一个锁结构,就是插入意向锁 锁内存结构 当然不是! 一个锁结构 如果被加锁的记录符合下面四条状态的话,那么这些记录的锁则会合到一个锁结构 中
在同一个事务中进行加锁操作 被加锁的记录在同一个页面中 加锁的类型是一样的 等待的状态是一样的 锁结构信息 然后我们再来依此看下这个所结构每个部分的信息都是什么意思
锁所在的事务信息 :无论是表级锁还是行级锁,一个锁属于一个事务,这里记载着该锁对应的信息索引信息 :对于行级锁来说,需要记录一下加锁的记录属于哪个索引表锁/行锁信息 :行级锁Space_ID :记录所在的表空间 *** Page Number**:记录所在的页号n_bits :一条记录对应着一个比特;一个页面包含多条记录,用不同的比特来区分到底是那一条记录加了锁,有个计算公式如下(公式中是取商)n_bits = (1+(n_recs+LOCK_PAGE_BITMAP_MARGIN)/ 8)x 8
LOCK_PAGE_BITMAP_MARGIN是固定的值为64,n_recs指当前界面一共有多少条记录(包含伪记录以及在垃圾链表中的记录),type_mode :32比特的数lock_mode (锁模式):低4比特位表示LOCK_AUTO_INC (十进制的4):表示AUTO-INC锁LOCK_IS (十进制的0):表示共享意向锁,IS锁LOCK_IX (十进制的1):表示独占意向锁,IX锁LOCK_S (十进制的2):表示共享锁,也就是S锁LOCK_X (十进制的3):表示独占锁,也就是X锁lock_type (锁类型):第5~8比特位表示LOCK_TABLE (十进制的1):当第5比特位设置为1时,表示表级锁LOCK_REC (十进制的32):当第6比特位设置为1时,表示行级锁rec_lock_type (行锁的具体类型):其余的比特位表示第9比特设置为1时,表示is_waiting为true ,即当前事务获取锁失败,处于等待状态 第9比特设置为0时,表示is_waiting为false ,即当前事务获取锁成功 LOCK_ORDINARY (十进制的0):表示next-key锁LOCK_GAP (十进制的512):当第10比特位是1时,表示gap锁LOCK_REC_NOT_GAP (十进制的1024):也就是当第11比特设置为1时,表示Record Lock(记录锁)LOCK_INSERT_INTENTION (十进制的2048):也就是当第12比特设置为1时,表示Insert Intention Lock(插入意向锁)LOCK_WAIT (十进制的256):也就是当其他信息:此文章不讨论 一堆比特位:此文章不讨论 举个例子 事务T1 要给user表中的记录加锁,假设这些记录存储在表空间号 为20,页号 为21的页面上,T1给id=1的记录加S型Record Lock锁,假如当前页面一共有5条记录(3条用户记录和2条伪记录)
过程:先给表加IS锁,不过我们现在不关心,只关心行级锁 , 具体生成的所结构如下图所示
最后 快过年啦,小杰可能也需要休息一下下,因为最近都周更(虽然上周有点事没更,打脸),周末完全没有其余时间了 感觉和朋友家人们联系有点少了,过年回家巩固下感情和朋友们聊聊天吹吹牛逼,顺便维护下峡谷的治安 最后祝关注java小杰要加油 的宝贝儿们 脱单暴富事事顺,升职加薪牛哄哄!