深入剖析 MySQL 自增锁
之前的文章把 InnoDB 中的所有的锁都介绍了一下,包括意向锁、记录锁...自增锁巴拉巴拉的。但是后面我自己回过头去看的时候发现,对自增锁的介绍居然才短短的一段。
其实自增锁(AUTO-INC Locks)这块还是有很多值得讨论的细节,例如在并发的场景下,InnoDB 是如何保证该值正确的进行自增的,本章就专门来简单讨论一下 InnoDB 中的自增锁。
什么是自增锁
之前我们提到过,自增锁是一种比较特殊的表级锁。并且在事务向包含了 AUTO_INCREMENT
列的表中新增数据时就会去持有自增锁,假设事务 A 正在做这个操作,如果另一个事务 B 尝试执行 INSERT
语句,事务 B 会被阻塞住,直到事务 A 释放自增锁。
这怎么说呢,说他对,但是他也不完全对。
行为与限制
其实上面说的那种阻塞情况只是自增锁行为的其中一种,可以理解为自增锁就是一个接口,其具体的实现有多种。具体的配置项为 innodb_autoinc_lock_mode
,通过这个配置项我们可以改变自增锁中运行的一些细节。
并且,自增锁还有一个限制,那就是被设置为 AUTO_INCREMENT
的列必须是索引,或者该列是索引的一部分(联合索引),不过这个限制对于大部分开发场景下并没有什么影响。
毕竟我们的基操不就是把 id 设置为
AUTO_INCREMENT
吗。
锁模式
其实在 InnoDB 中,把锁的行为叫做锁模式可能更加准确,那具体有哪些锁模式呢,如下:
传统模式(Traditional) 连续模式(Consecutive) 交叉模式(Interleaved)
分别对应配置项 innodb_autoinc_lock_mode
的值0、1、2.
看到这就已经知道为啥上面说不准确了,因为三种模式下,InnoDB 对并发的处理是不一样的,而且具体选择哪种锁模式跟你当前使用的 MySQL 版本还有关系。
在 MySQL 8.0 之前,InnoDB 锁模式默认为连续模式,值为1,而在 MySQL 8.0 之后,默认模式变成了交叉模式。至于为啥会改变默认模式,后面会讲。
传统模式
传统模式(Traditional),说白了就是还没有锁模式这个概念时,InnoDB 的自增锁运行的模式。只是后面版本更新,InnoDB 引入了锁模式的概念,然后 InnoDB 给了这种以前默认的模式一个名字,叫——传统模式。
传统模式具体是咋工作的?

我们知道,当我们向包含了 AUTO_INCREMENT
列的表中插入数据时,都会持有这么一个特殊的表锁——自增锁(AUTO-INC),并且当语句执行完之后就会释放。这样一来可以保证单个语句内生成的自增值是连续的。
这样一来,传统模式的弊端就自然暴露出来了,如果有多个事务并发的执行 INSERT
操作,AUTO-INC
的存在会使得 MySQL 的性能略有下降,因为同时只能执行一条 INSERT
语句。
连续模式
连续模式(Consecutive)是 MySQL 8.0 之前默认的模式,之所以提出这种模式,是因为传统模式存在影响性能的弊端,所以才有了连续模式。
在锁模式处于连续模式下时,如果 INSERT
语句能够提前确定插入的数据量,则可以不用获取自增锁,举个例子,像 INSERT INTO
这种简单的、能提前确认数量的新增语句,就不会使用自增锁,这个很好理解,在自增值上,我可以直接把这个 INSERT
语句所需要的空间流出来,就可以继续执行下一个语句了。
但是如果 INSERT
语句不能提前确认数据量,则还是会去获取自增锁。例如像 INSERT INTO ... SELECT ...
这种语句,INSERT
的值来源于另一个 SELECT
语句。
连续模式的图和交叉模式差不多
交叉模式
交叉模式(Interleaved)下,所有的 INSERT
语句,包含 INSERT
和 INSERT INTO ... SELECT
,都不会使用 AUTO-INC
自增锁,而是使用较为轻量的 mutex
锁。这样一来,多条 INSERT
语句可以并发的执行,这也是三种锁模式中扩展性最好的一种。

并发执行所带来的副作用就是单个 INSERT
的自增值并不连续,因为 AUTO_INCREMENT
的值分配会在多个 INSERT
语句中来回交叉的执行。
优点很明确,缺点是在并发的情况下无法保证数据一致性,这个下面会讨论。
交叉模式缺陷
要了解缺陷是什么,还得先了解一下 MySQL 的 Binlog。Binlog 一般用于 MySQL 的数据复制,通俗一点就是用于主从同步。在 MySQL 中 Binlog 的格式有 3 种,分别是:
Statement 基于语句,只记录对数据做了修改的SQL语句,能够有效的减少binlog的数据量,提高读取、基于binlog重放的性能 Row 只记录被修改的行,所以Row记录的binlog日志量一般来说会比Statement格式要多。基于Row的binlog日志非常完整、清晰,记录了所有数据的变动,但是缺点是可能会非常多,例如一条 update
语句,有可能是所有的数据都有修改;再例如alter table
之类的,修改了某个字段,同样的每条记录都有改动。Mixed Statement和Row的结合,怎么个结合法呢。例如像 alter table
之类的对表结构的修改,采用Statement格式。其余的对数据的修改例如update
和delete
采用Row格式进行记录。
如果 MySQL 采用的格式为 Statement
,那么 MySQL 的主从同步实际上同步的就是一条一条的 SQL 语句。如果此时我们采用了交叉模式,那么并发情况下 INSERT
语句的执行顺序就无法得到保障。
可能你还没看出问题在哪儿,INSERT
同时交叉执行,并且 AUTO_INCREMENT
交叉分配将会直接导致主从之间同行的数据主键 ID 不同。而这对主从同步来说是灾难性的。

换句话说,如果你的 DB 有主从同步,并且 Binlog 存储格式为 Statement,那么不要将 InnoDB 自增锁模式设置为交叉模式,会有问题。其实主从同步的过程远比上图中的复杂,之前我也写过详细的MySQL主从同步的文章,感兴趣可以先去看看。
而后来,MySQL 将日志存储格式从 Statement
变成了 Row
,这样一来,主从之间同步的就是真实的行数据了,而且 主键ID
在同步到从库之前已经确定了,就对同步语句的顺序并不敏感,就规避了上面 Statement
的问题。
基于 MySQL 默认 Binlog 格式从 Statement
到 Row
的变更,InnoDB 也将其自增锁的默认实现从连续模式,更换到了效率更高的交叉模式。
鱼和熊掌
但是如果你的 MySQL 版本仍然默认使用连续模式,但同时又想要提高性能,该怎么办呢?这个其实得做一些取舍。
如果你可以断定你的系统后续不会使用 Binlog,那么你可以选择将自增锁的锁模式从连续模式改为交叉模式,这样可以提高 MySQL 的并发。并且,没有了主从同步,INSERT
语句在从库乱序执行导致的 AUTO_INCREMENT
值不匹配的问题也就自然不会遇到了。
总结
你可能会说,为啥要了解这么深?有啥用?
其实还真有,例如在业务中你有一个需要执行 几十秒 的脚本,脚本中不停的调用多次 INSERT
,这时就问你这个问题,在这几十秒里,会阻塞其他的用户使用对应的功能吗?
如果你对自增锁有足够的了解,那么这个问题将会迎刃而解。
好了以上就是本篇博客的全部内容了,欢迎微信搜索关注【SH的全栈笔记】,回复【队列】获取MQ学习资料,包含基础概念解析和RocketMQ详细的源码解析,持续更新中。
如果你觉得这篇文章对你有帮助,还麻烦点个赞,关个注,分个享,留个言。

深入剖析 MySQL 自增锁的更多相关文章
- MySQL自增锁模式innodb_autoinc_lock_mode参数理解调优
前段时间某数据表运行过程中,出现自增字段突然跳跃式增长的问题,潜心研究发现,问题导致原因可能是因为并发写入导致 于是通过各种途径查阅是因为innodb_autoinc_lock_mode参数设置的不同 ...
- MySQL AutoIncrement--自增锁模式
自增锁模式 在MYSQL 5.1.22版本前,自增列使用AUTO_INC Locking方式来实现,即采用一种特殊的表锁机制来保证并发插入下自增操作依然是串行操作,为提高插入效率,该锁会在插入语句完成 ...
- MySQL自增锁等待问题解决
有网友再群里问:在做基准测试时候,批量插入数据时,有很多自增锁等待,我告诉他解决办法: 1.innodb_autoinc_lock_mode=2 2.innodb_autoextend_increme ...
- 从一个死锁看mysql innodb的锁机制
背景及现象 线上生产环境在某些时候经常性的出现数据库操作死锁,导致业务人员无法进行操作.经过DBA的分析,是某一张表的insert操 作和delete操作发生了死锁.简单介绍下数据库的情况(因为涉及到 ...
- MySQL/MariaDB的锁
本文目录: 1.MariaDB/MySQL事务提交的方式 2.MariaDB/MySQL中的锁简介 2.1 不同存储引擎支持的锁级别 2.2 锁类型 2.3 锁兼容性 3.MyISAM的表级锁(loc ...
- 详细介绍MySQL/MariaDB的锁
官方手册:https://dev.mysql.com/doc/refman/5.7/en/innodb-locking-transaction-model.html 1.事务提交的方式 在MariaD ...
- mysql中的锁
MYSQL不同的存储引擎支持不同的锁的机制 MyISAM 支持表锁,InnoDB支持表锁和行锁 表锁,行锁比较 表锁:开销小,加锁快:不会出现死锁:锁定力度大,发生锁冲突概率高,并发度最低 行锁:开销 ...
- 关于MySQL自增主键的几点问题(上)
前段时间遇到一个InnoDB表自增锁导致的问题,最近刚好有一个同行网友也问到自增锁的疑问,所以抽空系统的总结一下,这两个问题下篇会有阐述. 1. 划分三种插入类型 这里区分一下几种插入数据行的类型,便 ...
- MySQL里面的锁
MySQL里面的锁可以分为:全局锁,表级锁,行级锁. 一.全局锁:对整个数据库实例加锁.MySQL提供加全局读锁的方法:Flush tables with read lock(FTWRL)这个命令可以 ...
随机推荐
- Ancient Cipher UVA - 1339
Ancient Roman empire had a strong government system with various departments, including a secret s ...
- Nest 中处理 XML 类型的请求与响应
公众号及小程序的微信接口是通过 xml 格式进行数据交换的. 比如接收普通消息的接口: 当普通微信用户向公众账号发消息时,微信服务器将 POST 消息的 XML 数据包到开发者填写的 URL 上. - ...
- NNLM原理及Pytorch实现
NNLM NNLM:Neural Network Language Model,神经网络语言模型.源自Bengio等人于2001年发表在NIPS上的<A Neural Probabilistic ...
- 【Nginx(四)】Nginx配置集群 负载均衡策略
1.Nginx常见的负载均衡策略 ip_hash (固定分发) 简介:根据请求按访问ip的hash结果分配,这样每个用户就可以固定访问一个后端服务器 场景:服务器业务分区.业务缓存.Session需要 ...
- android之Tween Animation
android Tween Animation有四种,AlphaAnimation(透明度动画).ScaleAnimation(尺寸伸缩动画).TranslateAnimation(位移动画).Rot ...
- Web端直传数据至OSS
官方文档 最佳实践 小程序直传实践 支付宝小程序直传实践 微信小程序直传实践 Web端PostObject直传实践 Web端PostObject直传实践简介 JavaScript客户端签名直传 服务端 ...
- Mac下配置Git 的全局忽略文件
$ git config --global core.excludesfile ~/.gitignore_global $ vim ~/.gitignore_global #配置文件参考如下 # fo ...
- TLB和CPU缓存
TLB 如果每次应用程序访问一个线性地址都需要先解析(查PDT,PTT)那么效率十分低,为了提高执行效率CPU在CPU内部建立了一个TLB表,此表和寄存器一样访问速度极高.其会记录线性地址和物理地址之 ...
- 上手 WebRTC DTLS 遇到很多 BUG?浅谈 DTLS Fragment
上一篇<详解 WebRTC 传输安全机制:一文读懂 DTLS 协议>详细阐述了 DTLS.本文将结合 DTLS 开发中遇到的问题,详细解读 DTLS 的一些基础概念以及 Fragment ...
- JPEG头部解析
6.3 JPEG格式 6.3.1简介 微处理机中的存放顺序有正序(big endian)和逆序(little endian)之分.正序存放就是高字节存放在前低字节在后,而逆序存放就是低字 ...