哪里可做网站,七牛云对象存储,网站建设网课,网站建设工作室wp主题模板系列文章目录
学习MySQL先有全局观#xff0c;细说其发展历程及特点 Mysql常用操作#xff0c;谈谈排序与分页 拒绝零散碎片#xff0c; 一文理清MySQL的各种锁#xff08;收藏向#xff09; 系列文章目录一、MySQL的锁指什么二、排他与共享三、全局锁#xff08;Global…系列文章目录
学习MySQL先有全局观细说其发展历程及特点 Mysql常用操作谈谈排序与分页 拒绝零散碎片 一文理清MySQL的各种锁收藏向 系列文章目录一、MySQL的锁指什么二、排他与共享三、全局锁Global Lock四、表锁Table Lock五、意向锁Intention Locks六、行级锁Row Lock1. 记录锁Record Locks2. 间隙锁Gap Locks3. 临键锁Next-Key Locks4. 插入意向锁Insert Intention Locks5. 行锁总结 七、自增锁AUTO-INC Locks 相信大家在日常使用Mysql的过程中对锁也有着一些了解。什么排它/共享锁表锁、行锁还有意向锁之类的。相信同学们也看到过很多相关文章不过大部分文章只是偏重其中某一种锁进行深入分析实话实说确实有不少锁的逻辑较为复杂值得摸索。但对于大部分同学而言我认为最好是先对这些锁有个总体的认知做到先总再分这样才更能从全局了解锁的设计。 作者简介战斧从事金融IT行业有着多年一线开发、架构经验爱好广泛乐于分享致力于创作更多高质量内容 本文收录于 mysql 专栏有需要者可直接订阅专栏实时获取更新 高质量专栏 云原生、RabbitMQ、Spring全家桶 等仍在更新欢迎指导 Zookeeper Redis dubbo docker netty等诸多框架以及架构与分布式专题即将上线敬请期待 一、MySQL的锁指什么
我们先来看一下Mysql的基本构成如下图以前我们其实也介绍过MySQL数据库总体分为三个部分
Server层负责处理客户端连接、查询解析和优化、数据访问控制、事务处理、日志、replication和其他管理操作。存储引擎负责数据的存储和检索等操作。MySQL支持多个存储引擎如InnoDB、MyISAM、MEMORY等。物理磁盘层真正存储数据的位置保存着数据库数据以及各类日志。 我们日常说的MySQL的各种锁有的是由存储引擎提供的如行锁有些锁是有Server层提供的如全局锁、表锁它们结合起来才形成了完整的锁的体系
二、排他与共享
锁从不同角度理解其实会有不同的分类也就是说同一个东西在不同的分类下叫不同的锁但其实它们并不冲突。在讨论 MySQL 的锁时一般我们会有这样两种分类
锁的粒度是否排他
从锁的粒度来看可以分为全局锁、表锁和行锁顾名思义锁的层级分别是数据库、某张表、以及表里的行。 从是否独占来看可以分为排它锁、共享锁
在innoDB对于同一个资源是允许设定 排它锁(X) 和 共享锁(S) 两种的它们的兼容关系如下
XSX冲突冲突S冲突兼容
比如事务A对某一行上了共享锁(S)此时再来一个事务B如果B需要这个资源的共享锁那么它能立即获得。如果B需要该资源的排它锁事务B就需要等待了。
而不同粒度的锁接下来我们以一一讲解
三、全局锁Global Lock
用于 限制整个数据库实例 的访问当执行一些需要全局一致性的操作时例如备份、恢复等可以使用全局锁如下命令 FLUSH TABLES WITH READ LOCK这个命令会对所有数据库的所有表都上一个读锁加完这个锁后所有的表都会被锁定从而无法插入任何内容了mysql自己的系统日志表不在此列
四、表锁Table Lock
锁定整个表在执行涉及整个表的操作时会对整个表进行锁定避免其他事务对该表进行并发操作。它的上锁及解锁语法如下
-- 加锁
LOCK {TABLE | TABLES}tbl_name [[AS] alias] lock_type[, tbl_name [[AS] alias] lock_type] ...
lock_type: {READ [LOCAL]| WRITE
}-- 释放锁
UNLOCK {TABLE | TABLES}一般情况下不建议使用这种表锁除非是对一组MyISAM的表来操作那么提前将它们锁定会提高效率
五、意向锁Intention Locks
InnoDB 支持多粒度锁允许行锁和表锁共存。例如下面的语句就可以在指定的表上使用排他锁(X锁)
LOCK TABLES … WRITE而为了同时避免遍历行锁的困境比如事务A获取了某一行的排它锁此时事务B想锁表就必须遍历每一行检查是否有锁同时也为了避免不同粒度间的锁出现死锁所以InnoDB使用了意向锁。意向锁是表级别的锁它指示事务以后对表中的某一行需要哪种类型的锁(共享的还是排他的)。有两种类型的意向锁
意向共享锁(IS)表明事务打算对表中的单个行设置共享锁。意向排他锁(IX)表示事务打算对表中的单个行设置排他锁。
例如SELECT…FOR SHARE 设置IS锁SELECT…FOR UPDATE 设置一个IX锁。 在事务获得表中某一行的共享锁之前它必须首先获得表上的IS锁或更强的锁。 在事务获得表中某一行的排他锁之前它必须首先获得该表上的IX锁
表级锁类型兼容性如下表所示
XIXSISX冲突冲突冲突冲突IX冲突兼容冲突兼容S冲突冲突兼容兼容IS冲突兼容兼容兼容
这个表的理解其实很简单 1X 作为表的排他锁和其他所有表锁冲突 2IX 代表准备修改某一行此时如果有人持有表不论是 X 还是 S为了防止冲突或不一致所以会返回冲突
如果锁与现有锁兼容则将锁授予请求事务但如果它与现有锁冲突则不会授予。事务等待直到冲突的现有锁被释放。如果锁请求与现有锁冲突并且由于会导致死锁而无法授予则会发生错误。
意向锁的主要目的是显示某人正在锁定表中的一行或者将要锁定表中的一行作为行锁前置的隐式锁。也就是说若你仅使用表锁或仅使用行锁意向锁是不会让你阻塞的。只有你在持有行锁的情况下又使用表锁它才能发挥它的用处
六、行级锁Row Lock
在InnoDB存储引擎中默认使用的是行级锁。它可以实现更细粒度的并发控制只锁定部分行而不是整个表提高了并发性能。而从实际表现来说其又分为三种 记录锁Record Locks、间隙锁(Gap Locks),以及所谓临键锁(Next-Key Locks)。实际上在源码种这三种锁其实叫 LOCK_REC_NOT_GAP、LOCK_GAP、LOCK_ORDINARY /* Precise modes / /* this flag denotes an ordinary next-key lock in contrast to LOCK_GAP or LOCK_REC_NOT_GAP / constexpr uint32_t LOCK_ORDINARY 0; /* when this bit is set, it means that the lock holds only on the gap before the record; for instance, an x-lock on the gap does not give permission to modify the record on which the bit is set; locks of this type are created when records are removed from the index chain of records / constexpr uint32_t LOCK_GAP 512; /* this bit means that the lock is only on the index record and does NOT block inserts to the gap before the index record; this is used in the case when we retrieve a record with a unique key, and is also used in locking plain SELECTs (not part of UPDATE or DELETE) when the user has set the READ COMMITTED isolation level */ constexpr uint32_t LOCK_REC_NOT_GAP 1024; 1. 记录锁Record Locks
记录锁是索引记录上的锁例如
SELECT c1 FROM t WHERE c110 For update这样就能阻止任何其他事务插入、更新或删除c1是10为的行。 记录锁总是锁定索引即使定义的表没有索引也是如此。对于这样的情况InnoDB创建一个隐藏的聚集索引并使用该索引进行记录锁定
2. 间隙锁Gap Locks
如果我们把隔离级别设定为可重复度(RR)MySQL就会为我们解决”幻读“现象这里面除了MVCC的作用外还会在此时引入”间隙锁“的概念间隙锁本质上是 在索引记录之间的间隙上的锁或者在第一个索引记录之前或最后一个索引记录之后的间隙上的锁。比如说当我们执行这样一个SQL时
SELECT c1 FROM t WHERE c1 BETWEEN 10 and 20 For Update就会防止其他事务将值15插入到列t.c1中因为范围中所有现有值之间的间隙被锁定。
3. 临键锁Next-Key Locks
next-key锁是索引记录上的记录锁和索引记录之前的间隙上的间隙锁的组合。InnoDB执行行级锁的方式是当它搜索或扫描一个表索引时它会在遇到的索引记录上设置共享锁或排他锁。因此行级锁实际上是索引记录锁。索引记录上的next-key锁也会影响该索引记录之前的“间隙”。也就是说next-key锁是索引记录锁加上索引记录前面的间隙锁。
假设索引包含值10、11、13和20。此索引的Next-Key Locks锁定则覆盖以下区间其中圆括号表示不包含区间端点方括号表示包含端点:
(negative infinity, 10]
(10, 11]
(11, 13]
(13, 20]
(20, positive infinity)对于最后一个间隔next-key锁锁定索引中最大值以上的间隙以及值高于索引中任何实际值的“supremum”伪记录。”supremum“不是一个真正的索引记录因此实际上这个next-key锁只锁定最大索引值后面的间隙。默认情况下InnoDB运行在REPEATABLE READ事务隔离级别。在这种情况下InnoDB使用next-key锁进行搜索和索引扫描这可以防止幻行。
4. 插入意向锁Insert Intention Locks
学习了上面的间隙锁我们不难知晓想要插入新数据通常需要先获得间隙锁隔离级别为可重复读及以上。按照常理来说如果有多个事务都想往一个间隙里插入数据它们只要慢慢排队就好了但是我们还需要意识到一旦事务A插入一条数据成功原先的间隙可能就会变成两个间隙事务B可能又被A新诞生的这个间隙所阻碍。为了优化这种插入排队的情况innodb提出了插入意向锁的概念 An insert intention lock is a type of gap lock set by INSERT operations prior to row insertion. This lock signals the intent to insert in such a way that multiple transactions inserting into the same index gap need not wait for each other if they are not inserting at the same position within the gap 插入意图锁是insert操作在行插入之前设置的一种间隙锁。这个锁以这样一种方式表示插入的意图即插入到相同索引间隙中的多个事务如果不在间隙内的相同位置插入则不需要彼此等待 也就是说插入操作在获取间隙锁之前会先获取到插入意向锁下面的示例演示了一个事务在获得插入记录的排他锁之前使用插入意图锁。该示例涉及两个客户机A和b。客户机A创建一个包含两个索引记录(90和102)的表然后启动一个事务对ID大于100的索引记录设置排他锁。排他锁包括记录102之前的间隙锁:
mysql CREATE TABLE child (id int(11) NOT NULL, PRIMARY KEY(id)) ENGINEInnoDB;
mysql INSERT INTO child (id) values (90),(102);mysql START TRANSACTION;
mysql SELECT * FROM child WHERE id 100 FOR UPDATE;
-----
| id |
-----
| 102 |
-----客户端B开始一个事务向缺口间隙插入一条记录事务在等待获得排他锁时接受插入意图锁
mysql START TRANSACTION;
mysql INSERT INTO child (id) VALUES (101);在SHOW ENGINE INNODB STATUS和INNODB monitor输出中插入意图锁的事务数据如下所示
RECORD LOCKS space id 31 page no 3 n bits 72 index PRIMARY of table test.child
trx id 8731 lock_mode X locks gap before rec insert intention waiting
Record lock, heap no 3 PHYSICAL RECORD: n_fields 3; compact format; info bits 00: len 4; hex 80000066; asc f;;1: len 6; hex 000000002215; asc ;;2: len 7; hex 9000000172011c; asc r ;;...5. 行锁总结
其实对于行锁现在解析的文章有很多我们后续也可能结合源码进行更深入的解释。但在这里我们可以先进行总结(源于Mysql源码版本8.0.30)
锁基于扫描到的索引对于innoDB哪怕你没有显示建立索引每一张表也都至少会有一个索引即其聚集索引间隙锁Gap Locks 只有在隔离级别为可重复读以上才会有隔离级别为读未提交/读提交时行级锁默认使用记录锁Record Locks隔离级别为可重复读/序列化时行级锁默认使用的是临键锁Next-Key Locks间隙锁Gap Locks 的目的是不让这段间隙内插入新的数据因此其设计与传统意义上的共享/排他概念不一样。比如事务A在一个间隙上持有共享间隙锁(间隙s锁)而事务B可以在同一个间隙上持有排他性间隙锁(间隙x锁)
七、自增锁AUTO-INC Locks
AUTO-INC锁是一种特殊的表级锁用于在具有AUTO_INCREMENT列的表中插入事务。
在最简单的情况下如果一个事务正在向表中插入值那么任何其他事务都必须等待对该表进行自己的插入以便第一个事务插入的行接收连续的主键值。innodb_autoinc_lock_mode变量控制用于自动增量锁定的算法。它允许您选择如何在可预测的自动递增值序列和插入操作的最大并发性之间进行权衡。 这里有 0、1、2 三种模式可选 0traditional这是最常用的模式。在这种模式下InnoDB使用一个全局的互斥锁AUTO-INC锁来保护自增主键的访问。当有一个事务插入新记录时其他事务必须等待该事务释放AUTO-INC锁后才能插入新记录该锁通常保持到语句结束(而不是事务结束)。 1consecutive在这种模式下InnoDB使用一个递增的互斥锁AUTO-INC锁来保护自增主键的访问。如果我们可以预知插入的数据条数InnoDB会为每个事务分配一个独立的自增值区间。当一个事务需要插入自增值时它就可以在自己的区间内找到一个可用的自增值然后将其插入到表中。通过为每个事务分配独立的自增值区间Consecutive模式可以实现并行插入这种模式适用于具有高并发读写的应用可以提高性能。 2interleaved这种模式是在MySQL 8.0中引入的新模式。在这种模式下事务们都不再持有表级AUTO-INC锁该模式可以支持多个语句可以同时生成数字也就是说数字的分配在多个语句之间交错进行。
需要注意的是在我们使用它不同种类的插入语句的时候consecutive 自增锁有可能还是会使用全局互斥锁的样子因为不是每一种 insert 都能提前预知其数量的比如下面这样的插入
INSERT INTO t1 (c2) SELECT ... from another table ...