你们好, 我是公众号:java小杰要加油,
今天来分享一个关于mysql的知识点—— mysql中的锁
这个锁结构中有两个比较关键的信息(其实还有不少信息,后面再聊)java
Q: 能描述一下两个事务并发修改同一条数据时,mysql这个锁是怎么避免脏写的吗?A :事务T1在更改这条数据前,就先内存中生成一把锁与此数据相关联(is_waiting为false,表明没有等待),而后咔咔一顿操做更改数据,这个时候,事务T2来了,发现此记录已经有一把锁与之相关联了(就是T1那一把锁),而后就开始等待(is_waiting为true表明正在等待),事务T1更改完数据提交事务后,就会把此事务对应的所结构释放掉,而后检测一下还有没有与此记录相关联的锁,结果发现T2还在苦苦的等待,就把T2的锁结构的(is_waiting为false,表明没有等待)而后把T2事务对应的线程唤醒,T2获取锁成功继续执行,整体流程如上。mysql
在读-写 / 写 -读的状况下会出现脏读,不可重复读,幻读的现象,不一样的隔离级别能够避免不一样的问题,具体相关内容能够看小杰的这篇文章 京东面试官问我:“聊聊MySql事务,MVCC?” 面试
不过贴心的我仍是列出来了 注:√表明可能发生,×表明不可能发生sql
隔离级别 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
读未提交(read uncommitted RU) | √ | √ | √ |
读提交(read committed RC) | × | √ | √ |
可重复读(repeatable read RR) | × | × | √ |
串行化(serializable ) | × | × | × |
可是 RR在某些程度上避免了幻读的发生并发
怎么避免脏读、不可重复读、幻读这些现象呢?其实有两种方案mvc
- mvcc里面最重要的莫过于ReadView了,它的存在保证了事务不能够读取到未提交的事务所做的更改,避免了脏读。
- 在RC隔离级别下,每次select读操做都会生成ReadView
- 在RR隔离级别下,只有第一次select读操做才会生成ReadView,以后的select读操做都复用这一个ReadView
某些业务场景不容许读取旧记录的值,每次读取都要读取最新的值。
例如银行取款事务中,先把余额读取出来,再对余额进行操做。当这个事务在读取余额时,不容许其余事务对此余额进行访问读取,直到取款事务结束后才能够访问余额。因此在读数据的时候也要加锁
当使用 读写都加锁这个方案来避免并发事务 写-写、 读-写、 写-读时而产生的 脏读, 不可重复读, 幻读现象时,那么这个锁它就要作到,读读时不相互影响,上面三种状况时要相互阻塞,这时锁也分了好几类,咱们继续往下看
他们之间兼容关系以下 √表明能够兼容,×表明不可兼容jvm
兼容性 | S锁 | X锁 |
---|---|---|
S锁 | √ | × |
X锁 | × | × |
SELECT .. LOCK IN SHARE MODE # 对读取的记录添加S锁 SELECT .. FOR UPDATE # 对读取的记录添加X锁
前面提到的锁都是针对记录的,其实一个事务也能够在表级进行加锁(S锁、X锁)ui
但是怎么可能无缘无故的就给表加锁呢,难道没什么条件吗?答案是确定有条件的spa
可是这个怎么确保呢?难道要一行一行的遍历表中的全部数据吗?固然不是啦,聪明的大佬们想出了下面这两把锁线程
- 意向共享锁(Intention Shared Lock):简称IS锁,当事务准备在某记录上加S锁时,须要先在表级别加上一个IS锁
- 意向独占锁(Intention Exclusive Lock):简称IX锁,当事务准备在某记录上加X锁时,须要先在表级别加上一个IX锁
让咱们来看下加上这两把锁以后的效果是什么样子的
而后 通过上面的操做以后
这几种锁的兼容性以下表
兼容性 | IS锁(表级锁) | S锁 | IX锁(表级锁) | X锁 |
---|---|---|---|---|
IS锁(表级锁) | √ | √ | √ | × |
S锁 | √ | √ | × | × |
IX锁(表级锁) | √ | × | √ | × |
X锁 | × | × | × | × |
- IS、IX锁都是表级锁,他们能够共存。
- 他们的提出仅仅是为了在以后加表级别的S锁或者X锁时能够快速判断表中的记录是否被上锁,避免用遍历的方式来查看一行一行的去查看而已
若是被加锁的记录符合下面四条状态的话,那么这些记录的锁则会合到一个锁结构中
而后咱们再来依此看下这个所结构每一个部分的信息都是什么意思
表锁/行锁信息:行级锁
* Page Number:记录所在的页号
n_bits = (1+(n_recs+LOCK_PAGE_BITMAP_MARGIN)/ 8)x 8
LOCK_PAGE_BITMAP_MARGIN是固定的值为64,n_recs指当前界面一共有多少条记录(包含伪记录以及在垃圾链表中的记录),lock_mode(锁模式):低4比特位表示
lock_type(锁类型):第5~8比特位表示
rec_lock_type(行锁的具体类型):其他的比特位表示
LOCK_WAIT(十进制的256):也就是当
事务T1 要给user表中的记录加锁,假设这些记录存储在表空间号为20,页号为21的页面上,T1给id=1的记录加S型Record Lock锁,假如当前页面一共有5条记录(3条用户记录和2条伪记录)
过程:先给表加IS锁,不过咱们如今不关心,只关心行级锁,
具体生成的所结构以下图所示