深入剖析 MySQL 自增锁
来源:SegmentFault
时间:2023-01-09 18:38:21 231浏览 收藏
在数据库实战开发的过程中,我们经常会遇到一些这样那样的问题,然后要卡好半天,等问题解决了才发现原来一些细节知识点还是没有掌握好。今天golang学习网就整理分享《深入剖析 MySQL 自增锁》,聊聊MySQL、InnoDB,希望可以帮助到正在努力赚钱的你。
之前的文章把 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,这时就问你这个问题,在这几十秒里,会阻塞其他的用户使用对应的功能吗?
如果你对自增锁有足够的了解,那么这个问题将会迎刃而解。
本篇文章已放到我的 Github github.com/sh-blog 中,欢迎 Star。微信搜索关注【SH的全栈笔记】,回复【队列】获取MQ学习资料,包含基础概念解析和RocketMQ详细的源码解析,持续更新中。
如果你觉得这篇文章对你有帮助,还麻烦点个赞,关个注,分个享,留个言。
以上就是本文的全部内容了,是否有顺利帮助你解决问题?若是能给你带来学习上的帮助,请大家多多支持golang学习网!更多关于数据库的相关知识,也可关注golang学习网公众号。
-
499 收藏
-
244 收藏
-
235 收藏
-
157 收藏
-
101 收藏
-
288 收藏
-
480 收藏
-
430 收藏
-
120 收藏
-
344 收藏
-
481 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 542次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 507次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 497次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 484次学习