把MySQL中的各种锁及其原理都画出来( 二 )


把MySQL中的各种锁及其原理都画出来

文章插图
 
这种场景下的锁的释放较为复杂 , 有多种的优化方式 , 我对这块暂时还没有了解 , 还请知道的小伙伴在下方留言解释 。
下面主要依次介绍 InnoDB 中锁的模式和类型 , 锁的类型是指锁的粒度或者锁具体加在什么地方;而锁模式描述的是锁的兼容性 , 也就是加的是什么锁 , 比如写锁或者读锁 。
内容基本来自于 MySQL 的技术文档 innodb-lock 一章 , 感兴趣的同学可以直接去阅读原文 , 原文地址为见文章末尾 。
行锁的模式锁的模式有:读意向锁 , 写意向锁 , 读锁 , 写锁和自增锁(auto_inc) , 下面我们依次来看 。
读写锁读锁 , 又称共享锁(Share locks , 简称 S 锁) , 加了读锁的记录 , 所有的事务都可以读取 , 但是不能修改 , 并且可同时有多个事务对记录加读锁 。
写锁 , 又称排他锁(Exclusive locks , 简称 X 锁) , 或独占锁 , 对记录加了排他锁之后 , 只有拥有该锁的事务可以读取和修改 , 其他事务都不可以读取和修改 , 并且同一时间只能有一个事务加写锁 。
读写意向锁由于表锁和行锁虽然锁定范围不同 , 但是会相互冲突 。所以当你要加表锁时 , 势必要先遍历该表的所有记录 , 判断是否加有排他锁 。这种遍历检查的方式显然是一种低效的方式 , MySQL 引入了意向锁 , 来检测表锁和行锁的冲突 。
意向锁也是表级锁 , 也可分为读意向锁(IS 锁)和写意向锁(IX 锁) 。当事务要在记录上加上读锁或写锁时 , 要首先在表上加上意向锁 。这样判断表中是否有记录加锁就很简单了 , 只要看下表上是否有意向锁就行了 。
意向锁之间是不会产生冲突的 , 也不和 AUTO_INC 表锁冲突 , 它只会阻塞表级读锁或表级写锁 , 另外 , 意向锁也不会和行锁冲突 , 行锁只会和行锁冲突 。
自增锁AUTOINC 锁又叫自增锁(一般简写成 AI 锁) , 是一种表锁 , 当表中有自增列(AUTOINCREMENT)时出现 。当插入表中有自增列时 , 数据库需要自动生成自增值 , 它会先为该表加 AUTOINC 表锁 , 阻塞其他事务的插入操作 , 这样保证生成的自增值肯定是唯一的 。AUTOINC 锁具有如下特点:
  • AUTO_INC 锁互不兼容 , 也就是说同一张表同时只允许有一个自增锁;
  • 自增值一旦分配了就会 +1 , 如果事务回滚 , 自增值也不会减回去 , 所以自增值可能会出现中断的情况 。
显然 , AUTOINC 表锁会导致并发插入的效率降低 , 为了提高插入的并发性 , MySQL 从 5.1.22 版本开始 , 引入了一种可选的轻量级锁(mutex)机制来代替 AUTOINC 锁 , 可以通过参数 innodbautoinclockmode 来灵活控制分配自增值时的并发策略 。具体可以参考 MySQL 的 AUTOINCREMENT Handling in InnoDB 一文 , 链接在文末 。
不同模式锁的兼容矩阵下面是各个表锁之间的兼容矩阵 。
把MySQL中的各种锁及其原理都画出来

文章插图
 
总结起来有下面几点:
  • 意向锁之间互不冲突;
  • S 锁只和 S/IS 锁兼容 , 和其他锁都冲突;
  • X 锁和其他所有锁都冲突;
  • AI 锁只和意向锁兼容;
行锁的类型根据锁的粒度可以把锁细分为表锁和行锁 , 行锁根据场景的不同又可以进一步细分 , 依次为 Next-Key Lock , Gap Lock 间隙锁 , Record Lock 记录锁和插入意向 GAP 锁 。
不同的锁锁定的位置是不同的 , 比如说记录锁只锁住对应的记录 , 而间隙锁锁住记录和记录之间的间隔 , Next-Key Lock 则所属记录和记录之前的间隙 。不同类型锁的锁定范围大致如下图所示 。
把MySQL中的各种锁及其原理都画出来

文章插图
 
下面我们来依次了解一下不同的类型的锁 。
记录锁记录锁是最简单的行锁 , 并没有什么好说的 。上边描述 InnoDB 加锁原理中的锁就是记录锁 , 只锁住 id = 49 或者 name = 'Tom' 这一条记录 。


推荐阅读