InnoDB存储引擎
引擎区别
索引和算法
锁的机制
关键特性
事务一:重做日志
事务二:undo log
异步复制和半同步复制
MySQL主从配置参数
GTID
MySQL操作手册
自增ID和UUID 对性能的影响
本文档使用 MrDoc 发布
-
+
首页
锁的机制
## 锁的三种算法 - 行锁 Record Lock - 间隙锁 Gap Lock - Next-Key Lock:同时使用行锁和间隙锁 #### 什么是间隙锁 先来看看B+数索引的树结构:  从上图可以看出,索引树分为主索引树和辅助索引树,辅助索引树的叶子节点包含了主索引的key。主索引的key影响着辅助索引树叶子节点的排序。 **间隙锁的目的在于,锁住叶子节点的之间的间隙,不允许新的数据插入进来,从而防止幻读。** > 参考:[深入了解mysql--gap locks,Next-Key Locks](https://blog.csdn.net/qq_20597727/article/details/87308709) #### 哪些情况会产生间隙锁 - 如果查询条件是唯一索引且数据存在,只会加行锁; > 此时不存在幻读问题,因此不需要间隙锁。 - 如果查询条件是唯一索引但数据不存在,会产生间隙锁,锁住该数据存在的区间; > 锁住该区间,防止其他事务插入该条数据,就可以防止幻读了。 - 如果查询条件是普通索引而不是唯一索引,会在前后各加一个间隙锁; > select * from t where d=3 for update;(d列有非唯一索引) > 会产生行锁:d = 3 > 会产生间隙锁:(1,3), (3,6) > 产生这个间隙锁主要还是为了防止其他事务插入d=3的数据,因为从索引树结构来看,新插入的d=3的数据肯定会位于当前索引节点的前面或者后面,因此锁住这个间隙就可以防止插入,从而防止幻读。 > 行锁只能用于防止数据被修改,不能防止其他事务添加相同的数据。 > 同时有行锁和间隙锁,这种情况被称为 Next-Key Lock > Next-Key Lock才能最终防止幻读(解决不可重复读问题),既能防止数据被其他事务修改(行锁),又能防止其他事务插入新的数据(间隙锁); - 如果查询条件不是索引,会在主索引树上对全间隙添加间隙锁; > 如果查询条件不是索引,那么应该是在主索引树的叶子节点顺序遍历以查询到结果,数据可能会存在于其中的任何一个节点中。因此需要对全间隙添加间隙锁。 ## 一致性非锁定读 普通的select语句属于非锁定读,因为它不会对数据加锁。那它如何保证数据的一致性呢?主要是通过多版本控制(MVCC)。select读取的其实是数据的快照: * 在RR隔离级别下,读取的是事务开始时的快照; * 在RC隔离级别下,读取的是最新的快照; 这两者的区别在于,RC隔离级别下,可以读取到其他事务提交的数据;在RR隔离级别下,事务开始后,即便其他事务有提交数据,也是读取不到的。 数据的快照其实就是undo日志。读取数据快照是不需要上锁的,因为没有事务需要对历史数据进行修改。 ## 一致性锁定读 前面说过,select属于非锁定读。当然,我们也有办法使得select成为锁定读。锁定读的目的,主要是为了保证数据的正确性,比如读取库存并扣除库存的过程。InnoDB引擎对于select语句支持两种一致性的锁定读: * select ··· for update 会对读取的行添加X锁,其他事务不能对已锁定的行加任何锁 * select ··· lock in share mode 会对读取的记录行加S锁,其他事务可以对已锁定的行加S锁,但是不能加X锁,会被阻塞 ## 死锁 #### 什么是死锁 死锁是指两个或两个以上的事务在执行过程中,因争夺资源而造成的一种互相等待的现象。若无外力作用,各个事务都将无法进行下去。 #### 什么情况会产生死锁 案例一【最经典死锁场景】:加锁顺序导致死锁  死锁的原因在于多个事务对同一组数据加锁顺序不一致。 事务A获取记录id=20的锁 事务B获取记录id=30的锁 事务A获取记录id=30的锁,发现已经被B锁住,因此等待 事务B获取记录id=20的锁,发现已经被A锁住,因此等待 案例二:select for update 导致死锁 ```shell # 事务1 # 由于id=10000不存在,这里需要获取间隙锁,防止别人插入这条记录 select * from product where id = 10000 for update; update product set store = store - 1 where id = 10000; # insert 需要获取意向锁,意向锁需要等其他事务的间隙锁释放 insert into product (id, name) values (10001, "cup") ``` 如果id=10000的product 不存在,就会触发产生间隙锁:目的是防止其他事务插入id=10000的product,导致当前事务出现幻读现象。这时,如果多个进程同时执行类似的事务,他们会死锁,因为insert的时候,需要等其他事务的间隙锁释放。 > 参考:[解决死锁之路(终结篇) - 再见死锁](https://www.aneasystone.com/archives/2018/04/solving-dead-locks-four.html "解决死锁之路(终结篇) - 再见死锁") > 参考:[MySQL可重复读下 for update导致死锁](http://xiaoyue26.github.io/2020/06/08/2020-06/mysql%E5%8F%AF%E9%87%8D%E5%A4%8D%E8%AF%BB%E4%B8%8B%E7%94%A8for-update%E5%AF%BC%E8%87%B4%E7%9A%84%E6%AD%BB%E9%94%81/ "MySQL可重复读下 for update导致死锁") > 参考:[MySQL中for update的作用和用法](https://segmentfault.com/a/1190000023045909 "MySQL中for update的作用和用法") #### 如何解决死锁 一般来说,发生死锁后不需要我们手动处理,数据库会自己处理,我们只需要优化程序,防止死锁的发生。 数据库解决死锁主要有两种方法: - 超时机制 通过设置超时时间,当事务等待的时间达到这个值,对它进行回滚 - 主动检测死锁,并回滚undo量小的事务 主要通过wait-for graph(等待图)的方式来检测死锁。
gaojian
2021年6月21日 16:00
分享文档
收藏文档
上一篇
下一篇
微信扫一扫
复制链接
手机扫一扫进行分享
复制链接
关于 MrDoc
觅思文档MrDoc
是
州的先生
开发并开源的在线文档系统,其适合作为个人和小型团队的云笔记、文档和知识库管理工具。
如果觅思文档给你或你的团队带来了帮助,欢迎对作者进行一些打赏捐助,这将有力支持作者持续投入精力更新和维护觅思文档,感谢你的捐助!
>>>捐助鸣谢列表
微信
支付宝
QQ
PayPal
Markdown文件
分享
链接
类型
密码
更新密码