InnoDB自增原理都搞不清楚,还怎么CRUD?
qiyuwang 2024-11-08 14:36 13 浏览 0 评论
虽然我们习惯于给主键ID指定AUTO_INCREMENT属性,但是AUTO_INCREMENT也是可以指定到非主键字段的,唯一的约束就是这个字段上面得加索引,有了索引,就可以通过类似SELECT MAX(*ai_col*)的语句快速读到这列数据的最大值。
本文要探讨的话题是MySql的InnoDB引擎处理自增数据列的原理
MySql 5.1之前的实现
在这个版本之前,用AUTO_INCREMENT修饰的数据列确实是严格连续自增的。MySql的实现是会针对每个插入语句加一个全表维度的锁,这个锁可以保证每次只有一条插入语句在执行,每插入一行数据,就会生成一个自增数据。
mysql> CREATE TABLE t1 (
-> c1 INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
-> c2 CHAR(1)
-> ) ENGINE=InnoDB AUTO_INCREMENT=100;
假如我们在数据库中新建上面的这张表,接着我们执行插入语句。
mysql> INSERT INTO t1 (c1,c2) VALUES (NULL,'a'), (NULL,'b'), (NULL,'c'), (NULL,'d');
针对这条MySql执行的流程为:
全表加 AUTO-INC锁
1.1 生成主键ID:101
1.2 将行(101, 'a')插入表中
1.3 生成主键ID: 102
1.4 将行(102, 'b')插入表中
...
释放 AUTO-INC锁
MySql5.1之前的这种实现方式可以保证AUTO_INCREMENT严格自增,但是并发程度也最差,因为AUTO_INCREMENT锁是全表加锁直到这条语句结束
MySql 5.1版本带来的优化
前文中的insert语句是比较简单的,所谓简单的insert语句指的是插入的的数据行数是可以提前确定的,与之相对的是Bulk insert比如INSERT ... SELECT这类语句,这类插入语句的插入行数不能提前确定。
在这个版本以及之后,对于简单语句的插入,不再加全表的AUTO-INC锁,只会在产生自增列数据的时候加一个轻量级的互斥锁,等自增数据分配好,锁就释放了,因此像上面的例子,在MySql5.1之后的执行流程如下
加轻量级互斥锁
1.1 分配自增数据
释放锁
将行(101, 'a')插入表中
将行(102, 'b')插入表中
...
可以看到,对于简单的插入语句,并发情况下的临界区变小了,且不再持有全表的锁,提升了并发性能。当然,如果在尝试加锁的过程中遇到有其他事务持有全表的AUTO-INC锁,还是要等待全表的AUTO-INC锁释放再执行本次插入操作
对于Bulk insert的插入语句,仍然避免不了全局的AUTO-INC锁,这类语句,他们的执行流程仍然保持和5.1之前版本一致,比如以下表为例
CREATE TABLE t1 (
c1 INT(11) NOT NULL AUTO_INCREMENT,
c2 VARCHAR(10) DEFAULT NULL,
PRIMARY KEY (c1)
) ENGINE=InnoDB;
执行下面两条语句
Tx1: INSERT INTO t1 (c2) SELECT 1000 rows from another table ...
Tx2: INSERT INTO t1 (c2) VALUES ('xxx');
由于在执行Tx1时,InnoDB无法知道要插入的具体行数,因此会获取一个全表的锁,每执行一条插入语句就会给自增列赋新的值。因为有全表的锁,所以Tx1这条语句插入的所有行数都是连续自增的,Tx2自增列的值要么小于Tx1自增列的最小值,要么大于Tx1自增列中的最大值,这取决于这两条语句的执行顺序
InnoDB采取这样的决策一个重要的原因是主从复制,在MySql8.0之前,MySql的主从是基于语句复制的。在刚才的例子中,如果Tx1执行的时候没有全表的锁,那有可能在Tx1执行的过程中Tx2也在执行,这就会导致Tx1和Tx2自增列的数据每次执行结果都不相同,也就无法在从库中通过语句回放复制。
MySql 8.0版本之后的优化
虽然MySql5.1版本对简单的插入语句做了优化,避免了全表加锁,但对于INSERT ... SELECT这样的复杂插入语句,仍然避免不了全表的AUTO-INC锁,主要是基于执行语句的主从复制要能在从库完全回放复制主库,所有的语句执行结果就不能和执行顺序有关。
在MySql 8.0以及之后默认的主从复制策略变成了基于数据行实现,在这样的背景下INSERT ... SELECT这样的复杂插入语句也不需要全表加锁来生成自增列数据了,所有的插入语句只有在生成自增列数据的时候要求持有一个轻量级的互斥锁,等到自增数据生成好之后释放锁。在这种实现下,所有插入语句的自增列都不能保证连续自增,但是并发性能确实最好的。
总结
需要说明的是,如果插入语句所处的事务回滚了,生成的自增列数据是不会回滚的,这种情况下会造成自增列数据非连续增长。
以上所述都是各个MySql版本的默认实现,MySql 5.1引入了一个新的参数 innodb_autoinc_lock_mode 通过修改这个字段的值,可以改变InnoDB生成自增列的策略,其值总结如下:
不推荐显式指定自增列数据,因为在5.7以及之前的版本,如果通过update语句显式指定一个比SELECT MAX(*ai_col*)还大的自增列值,后续insert语句可能会抛"Duplicate entry"错误,这一点在8.0版本之后也有了改变,如果通过显式的update语句显式指定一个比SELECT MAX(*ai_col*)还大的自增列值,那该值就会被持久化,后续的自增列值都从该值开始生成。
假如有下面这张表
mysql> CREATE TABLE t1 (
-> c1 INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
-> c2 CHAR(1)
-> ) ENGINE = INNODB AUTO_INCREMENT=100;
试想,在我们执行完下面这条语句之后表的内容变成了什么?
mysql> INSERT INTO t1 (c1,c2) VALUES (1,'a'), (NULL,'b'), (5,'c'), (NULL,'d');
MySql 5.1之前,或者innodb_autoinc_lock_mode设置为0
mysql> SELECT c1, c2 FROM t1 ORDER BY c2;
+-----+------+
| c1 | c2 |
+-----+------+
| 1 | a |
| 101 | b |
| 5 | c |
| 102 | d |
+-----+------+
在这种模式下,每插入一行数据就会生成一个自增值赋到c1这一行,因此c1的下一个自增值是103
MySql 8.0之前,或者innodb_autoinc_lock_mode设置为1
mysql> SELECT c1, c2 FROM t1 ORDER BY c2;
+-----+------+
| c1 | c2 |
+-----+------+
| 1 | a |
| 101 | b |
| 5 | c |
| 102 | d |
+-----+------+
当前表的数据与前一个场景一致,但是下一个自增值却是105,因为在这个场景下,自增数据是在插入语句执行的最开始一次性生成的
MySql 8.0之后,或者innodb_autoinc_lock_mode设置为2
mysql> SELECT c1, c2 FROM t1 ORDER BY c2;
+-----+------+
| c1 | c2 |
+-----+------+
| 1 | a |
| x | b |
| 5 | c |
| y | d |
+-----+------+
在这种场景下,因为同时可能有其他的插入语句执行,因此x和y的值是不确定的,下一个自增值也是未知的。
相关推荐
- PPO 强化学习机械臂 IK 训练过程可视化利器 Tensorboard
-
视频讲解:PPO强化学习机械臂IK训练过程可视化利器Tensorboard_哔哩哔哩_bilibili...
- 免费定时运行Python程序并存储输出文档的服务推荐
-
免费定时运行Python程序并存储输出文档的服务推荐以下是几种可以免费定时运行Python程序并存储输出结果的云服务方案:1.PythonAnywhere特点:提供免费的Python托管环境...
- 实战搭建深度学习运行
-
一、准备环境ubuntu22.04nvidia显卡这里使用的是RTX3060已安装Python3.10二、安装pip3...
- 部署Django到阿里云服务器教程
-
基于Ubuntu16.04+Python3+nginx+mysql+Django接下来先安装这些必要的环境,这些操作都是在你已经购买了阿里云服务器。并且在本地你已经搭建好了Django...
- Ubuntu 22.04安装MySQL : Qwen2.5 模型对话数据收集与微调教程
-
1安装MySQL首先来到(安装)根目录下,开启终端...
- 3D点云数据处理简明教程【Python】
-
近年来,在AR和空间计算技术的大力推动下,在Apple和Meta等大公司的支持下,最近推出了AppleVisionPro,3D理解领域受到越来越多的关注。...
- Ubuntu22.04中定时执行任务(cron)设置
-
做这个实验,主要涉及内容如下:cronTest.py待执行的python文件,代码会读入本地文件内容,并打印当前时间cron_run.sh将执行cronTest.py的语句,包装成shellcro...
- 使用vllm部署DeepSeek
-
1、基础环境1.1、基础环境准备RTX3090*2卡ubuntu22.04python3.12cuda12.4pytorch2.5.1...
- 一键打包,随时运行,Python3项目虚拟环境一键整合包的制作(Venv)
-
之前我们介绍了如何使用嵌入式Python3环境给项目制作一键整合包,在使用嵌入式Python环境时,通常是作为另一个应用程序的一部分,而Python3虚拟环境是为了在开发过程中隔离项目所需的...
- 从零开始:Ubuntu Server中MySQL 8.0的安装与Django数据库配置详解
-
Ubuntu系统纯净安装MySQL8.01、安装Mysql8.0sudoaptinstallmysql-server...
- 同一台电脑如何共存多个版本Python?
-
同一台电脑可以安装多个版本Python吗?可以共存!当前电脑安装了Python3.8.832位,为了调用大漠插件dll,但是我又想安装较新版本的64位Python3.12.0,就涉及到多个Pytho...
- zabbix企业微信告警
-
zabbix企业微信告警的前提是用户有企业微信且创建了一个能够发送消息的应用,具体怎么创建可以协同用户侧企业微信的管理员。第一步:企业微信准备我们需要的内容包括企业ID,应用的AgentId和应用的S...
- 「2022 年」崔庆才 Python3 爬虫教程 - urllib 爬虫初体验
-
首先我们介绍一个Python库,叫做urllib,利用它我们可以实现HTTP请求的发送,而不用去关心HTTP协议本身甚至更低层的实现。我们只需要指定请求的URL、请求头、请求体等信息即...
- 手把手教你搭建深度学习环境Pytorch版-Ubuntu
-
引言很多搞人工智能的小伙伴,刚开始学习,往往摸不着头脑怎么跑代码。跑代码的前提是要有个环境。本篇结合自己的亲身经历,带你搭建环境。相关知识Ubuntu是Linux系统的一种...
你 发表评论:
欢迎- 一周热门
- 最近发表
- 标签列表
-
- navicat无法连接mysql服务器 (65)
- 下横线怎么打 (71)
- flash插件怎么安装 (60)
- lol体验服怎么进 (66)
- ae插件怎么安装 (62)
- yum卸载 (75)
- .key文件 (63)
- cad一打开就致命错误是怎么回事 (61)
- rpm文件怎么安装 (66)
- linux取消挂载 (81)
- ie代理配置错误 (61)
- ajax error (67)
- centos7 重启网络 (67)
- centos6下载 (58)
- mysql 外网访问权限 (69)
- centos查看内核版本 (61)
- ps错误16 (66)
- nodejs读取json文件 (64)
- centos7 1810 (59)
- 加载com加载项时运行错误 (67)
- php打乱数组顺序 (68)
- cad安装失败怎么解决 (58)
- 因文件头错误而不能打开怎么解决 (68)
- js判断字符串为空 (62)
- centos查看端口 (64)