🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
# 锁 ## 锁的种类 ![](https://img.kancloud.cn/a9/1b/a91bed62d6dade7f3683dc3044dfb3d2_989x429.png) Mysql中锁的分类按照不同类型的划分可以分成不同的锁,按照**「锁的粒度」**划分可以分成:**「表锁、页锁、行锁」**;按照**「使用的方式」**划分可以分为:**「共享锁」**和**「排它锁」**;按照思想的划分:**「乐观锁」**和**「悲观锁」**。 **锁的粒度?** **「表锁」**是粒度最大的锁,开销小,加锁快,不会出现死锁,但是由于粒度太大,因此造成锁的冲突几率大,并发性能低。 **「行锁」**是粒度最小的锁机制,行锁的加锁开销性能大,加锁慢,并且会出现死锁,但是行锁的锁冲突的几率低,并发性能高。 **「页锁」**的粒度是介于行锁和表锁之间的一种锁,因为页锁是在BDB中支持的一种锁机制,也很少没人提及和使用,所以这里制作概述,不做详解。 ## MyISAM 表锁模式有两种:**「表共享读锁」**和**「表独占写锁」**。 #### 「表共享读锁」 当一个线程获取到MyISAM表的读锁的时候,会阻塞其他用户对该表的写操作,但是不会阻塞其它用户对该用户的读操作。 #### 「表独占写锁」 当一个线程获取到MyISAM表的写锁的时候,就会阻塞其它用户的读写操作对其它的线程具有排它性。 // 显式的添加表级读锁 LOCK TABLE 表名 READ // 显示的添加表级写锁 LOCK TABLE 表名 WRITE // 显式的解锁(当一个事务commit的时候也会自动解锁) unlock tables; ## MyISAM表级锁的竞争 ![](https://img.kancloud.cn/6e/16/6e161dcc9ff557663cb70205b01509c1_1042x685.png) MyISAM存储引擎中,**「假如同时一个读请求,一个写请求过来的话,它会优先处理写请求」**,因为MyISAM存储引擎中认为写请求比读请求重要。 这样就会导致,**「假如大量的读写请求过来,就会导致读请求长时间的等待,或者"线程饿死",因此MyISAM不适合运用于大量读写操作的场景」**,这样会导致长时间读取不到用户数据,用户体验感极差。 当然可以通过设置`low-priority-updates`参数,设置请求链接的优先级,使得Mysql优先处理读请求。 ## InnoDB 除了有**「表锁」**和**「行级锁」**的概念,还有Gap Lock(间隙锁)、Next-key Lock锁,**「间隙锁主要用于范围查询的时候,锁住查询的范围,并且间隙锁也是解决幻读的方案」**。 #### #### Gap Lock(间隙锁) ![](https://img.kancloud.cn/a5/59/a559fc84bb80527d24fa150c9a1e8b72_1023x856.png) **「从上面的测试结果显示在区间(1,3\]U\[3,5)之间加了锁,是不能够新增数据行,这就是新增num=2和num=4失败的原因,但是在这个区间以外的数据行是没有加锁的,可以新增数据行」**。 根据索引的有序性,而普通索引是可以出现重复值,那么当我们第一个sesson查询的时候只出现一条数据num=3,为了解决第二次查询的时候出现幻读,也就是出现两条或者更多num=3这样查询条件的数据。 Mysql在满足where条件的情况下,给`(1,3]U[3,5)`区间加上了锁不允许插入num=3的数据行,这样就解决了幻读。 注:间隙范围的确定:当前锁定的数据的值,找到距离这个值最近的最大最小值;例如:锁定ID=10;表里最近的数据 最小id=11 最大id=18,那么这个范围就是(11,10]U[10,18) **「主键索引(唯一索引)是否会加上间隙锁呢?」** 因为主键索引具有唯一性,不允许出现重复,那么当进行等值查询的时候只能有且只有一条数据,因此它只要锁定这条数据(锁定索引),在下次查询当前读的时候不会被删除、或者更新该数据行,也就保证了数据的一致性,所以主键索引由于他的唯一性的原因,**是不需要加间隙锁的。** **「范围查询是否会加上间隙锁?」** 会加间隙锁 **「使用不存在的检索条件是否会加上间隙锁?」** 会加间隙锁 ### next-key lock **行锁和间隙锁共同组成** https://www.jianshu.com/p/32904ee07e56 InnoDB中的行级锁是**「对索引加的锁,在不通过索引查询数据的时候,InnoDB就会使用表锁」**。 **「但是通过索引查询的时候是否使用索引,还要看Mysql的执行计划」**,Mysql的优化器会判断是一条sql执行的最佳策略。 若是Mysql觉得执行索引查询还不如全表扫描速度快,那么Mysql就会使用全表扫描来查询,这是即使sql语句中使用了索引,最后还是执行为全表扫描,加的是表锁。 **「共享读锁(S锁)**「和」**排它写锁(X锁)」**。 #### 「共享读锁(S锁)」 当一个事务对Mysql中的一条数据行加上了S锁,当前事务不能修改该行数据只能执行读操作,其他事务只能对该行数据加S锁不能加X锁。 #### 排它写锁(X锁) 若是一个事务对一行数据加了X锁,该事务能够对该行数据执行读和写操作,其它事务不能对该行数据加任何的锁,既不能读也不能写。 Mysql的**「悲观锁的实现是基于Mysql自身的锁机制实现,而乐观锁需要程序员自己去实现的锁机制」**,最常见的乐观锁实现就锁机制是**「使用版本号实现」**。 排他写锁(悲观锁):select ... for update; 共享读锁:select ... lock in share mode; ### 悲观锁优点与不足 悲观并发控制实际上是“先取锁再访问”的保守策略,为数据处理的安全提供了保证。但是在效率方面,处理加锁的机制会让数据库产生额外的开销,还有增加产生死锁的机会;另外,在只读型事务处理中由于不会产生冲突,也没必要使用锁,这样做只能增加系统负载;还有会降低了并行性,一个事务如果锁定了某行数据,其他事务就必须等待该事务处理完才可以处理那行数。 参考: [https://zhuanlan.zhihu.com/p/31875702] [https://blog.csdn.net/qq\_38238296/article/details/88362999]