当前位置:首页 > 文章列表 > 数据库 > MySQL > 深入剖析 MySQL 自增锁

深入剖析 MySQL 自增锁

来源:SegmentFault 2023-01-09 18:38:21 0浏览 收藏

在数据库实战开发的过程中,我们经常会遇到一些这样那样的问题,然后要卡好半天,等问题解决了才发现原来一些细节知识点还是没有掌握好。今天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学习网公众号。

版本声明
本文转载于:SegmentFault 如有侵犯,请联系study_golang@163.com删除
工具 | 使用 CLion 编译调试 MySQL 8.0工具 | 使用 CLion 编译调试 MySQL 8.0
上一篇
工具 | 使用 CLion 编译调试 MySQL 8.0
Ubuntu搭建Mysql+Keepalived高可用(双主热备)
下一篇
Ubuntu搭建Mysql+Keepalived高可用(双主热备)
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之JavaScript设计模式
    前端进阶之JavaScript设计模式
    设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
    542次学习
  • GO语言核心编程课程
    GO语言核心编程课程
    本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
    508次学习
  • 简单聊聊mysql8与网络通信
    简单聊聊mysql8与网络通信
    如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
    497次学习
  • JavaScript正则表达式基础与实战
    JavaScript正则表达式基础与实战
    在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
    487次学习
  • 从零制作响应式网站—Grid布局
    从零制作响应式网站—Grid布局
    本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
    484次学习
查看更多
AI推荐
  • 笔灵AI生成答辩PPT:高效制作学术与职场PPT的利器
    笔灵AI生成答辩PPT
    探索笔灵AI生成答辩PPT的强大功能,快速制作高质量答辩PPT。精准内容提取、多样模板匹配、数据可视化、配套自述稿生成,让您的学术和职场展示更加专业与高效。
    14次使用
  • 知网AIGC检测服务系统:精准识别学术文本中的AI生成内容
    知网AIGC检测服务系统
    知网AIGC检测服务系统,专注于检测学术文本中的疑似AI生成内容。依托知网海量高质量文献资源,结合先进的“知识增强AIGC检测技术”,系统能够从语言模式和语义逻辑两方面精准识别AI生成内容,适用于学术研究、教育和企业领域,确保文本的真实性和原创性。
    23次使用
  • AIGC检测服务:AIbiye助力确保论文原创性
    AIGC检测-Aibiye
    AIbiye官网推出的AIGC检测服务,专注于检测ChatGPT、Gemini、Claude等AIGC工具生成的文本,帮助用户确保论文的原创性和学术规范。支持txt和doc(x)格式,检测范围为论文正文,提供高准确性和便捷的用户体验。
    30次使用
  • 易笔AI论文平台:快速生成高质量学术论文的利器
    易笔AI论文
    易笔AI论文平台提供自动写作、格式校对、查重检测等功能,支持多种学术领域的论文生成。价格优惠,界面友好,操作简便,适用于学术研究者、学生及论文辅导机构。
    40次使用
  • 笔启AI论文写作平台:多类型论文生成与多语言支持
    笔启AI论文写作平台
    笔启AI论文写作平台提供多类型论文生成服务,支持多语言写作,满足学术研究者、学生和职场人士的需求。平台采用AI 4.0版本,确保论文质量和原创性,并提供查重保障和隐私保护。
    35次使用
微信登录更方便
  • 密码登录
  • 注册账号
登录即同意 用户协议隐私政策
返回登录
  • 重置密码