关于MySQL自增主键的几点问题(上)
前段时间遇到一个InnoDB表自增锁导致的问题,最近刚好有一个同行网友也问到自增锁的疑问,所以抽空系统的总结一下,这两个问题下篇会有阐述。
1. 划分三种插入类型
这里区分一下几种插入数据行的类型,便于后面描述:(纯逻辑上的划分)
“Simple inserts”
简单插入,就是在处理sql语句的时候,能够提前预估到插入的行数,包括INSERT/REPLACE的单行、多行插入,但不含嵌套子查询以及INSERT ... ON DUPLICATE KEY UPDATE。“Bulk inserts”
本文暂且叫做 大块插入,不能提前预知语句要插入的行数,也就无法知道分配多少个自增值,包括INSERT ... SELECT,REPLACE ... SELECT, 以及LOAD DATA导入语句。InnoDB会每处理一行记录就为 AUTO_INCREMENT 列分配一个值。“Mixed-mode inserts”
混合插入,比如在 “简单插入” 多行记录的时候,有的新行有指定自增值,有的没有,所以获得最坏情况下需要插入的数量,然后一次性分配足够的auto_increment id。比如:1
2# c1 是 t1 的 AUTO_INCREMENT 列
INSERT INTO t1 (c1,c2) VALUES (1,'a'), (NULL,'b'), (5,'c'), (NULL,'d');
又比如 INSERT ... ON DUPLICATE KEY UPDATE,它在 update 阶段有可能分配新的自增id,也可能不会。
2. 三种自增模式:innodb_autoinc_lock_mode
在以 5.6 版本,自增id累加模式分为:
传统模式
traditional,innodb_autoinc_lock_mode = 0
在具有 AUTO_INCREMENT 的表上,所有插入语句会获取一个特殊的表级锁 AUTO-INC ,这个表锁是在语句结束之后立即释放(无需等到事务结束),它可以保证在一个insert里面的多行记录连续递增,也能保证多个insert并发情况下自增值是连续的(不会有空洞)。连续模式
consecutive,innodb_autoinc_lock_mode = 1
MySQL 5.1.22开始,InnoDB提供了一种轻量级互斥的自增实现机制,在内存中会有一个互斥量(mutex),每次分配自增长ID时,就通过估算插入的数量(前提是必须能够估算到插入的数量,否则还是使用传统模式),然后更新mutex,下一个线程过来时从新 mutex 开始继续计算,这样就能避免传统模式非要等待每个都插入之后才能获取下一个,把“锁”降级到 只在分配id的时候 锁定互斥量。
在innodb_autoinc_lock_mode = 1(默认) 模式下,“简单插入”采用上面的 mutex 方式,“大块插入”(insert/replace … select … 、load data…)依旧采用 AUTO-INC 表级锁方式。当然如果一个事务里已经持有表 AUTO-INC 锁,那么后续的简单插入也需要等待这个 AUTO-INC 锁释放。这能够保证任意insert并发情况下自增值是连续的。交叉模式
interleaved,innodb_autoinc_lock_mode = 2
该模式下所有 INSERT SQL 都不会有表级 AUTO-INC 锁,多个 语句 可以同时执行,所以在高并发插入场景下性能会好一些。但是当 binlog 采用 SBR 格式时,对于从库重放日志或者主库实例恢复时,并不可靠。
另者,它只能保证自增值在 insert语句级别 (单调)递增,所以多个insert可能会交叉着分配id,最终可能导致多个语句之间的id值不连续,这种情况出现在 混合插入:1
INSERT INTO t1 (c1,c2) VALUES (1,'a'), (NULL,'b'), (5,'c'), (NULL,'d');
mutex 会按行分配4个id,但实际只用到2个,因此出现空洞。
3. 自增空洞(auto-increment sequence gap)
关于 AUTO_INCREMENT 自增出现空洞的问题,有必要再说明一下。
在 0, 1, 2 三种任何模式下,如果事务回滚,那么里面获得自增值的sql回滚,但产生的自增值会一起丢失,不可能重新分配给其它insert语句。这也会产生空洞。
在大块插入情景下
innodb_autoinc_lock_mode为 0 或 1 时,因为 AUTO-INC 锁会持续到语句结束,同一时间只有一个 语句 在表上执行,所以自增值是连续的(其它事务需要等待),不会有空洞;innodb_autoinc_lock_mode为 2 时,两个 “大块插入” 之间可能会有空洞,因为每条语句事先无法预知精确的数量而导致分配过多的id,可能有空洞。
4. 混合插入对 AUTO_INCREMENT 的影响
混合插入在 innodb_autoinc_lock_mode 不同模式下会有对 表自增值有不同的表现。
1 |
CREATE TABLE t1 ( |
1. mode 0
1 |
mysql> select @@innodb_autoinc_lock_mode; |
可以看到下一个自增值是 103 ,因为即使这是 一条 insert语句(多行记录),自增值还是每次分配一个,不会在语句开始前一次分配全。
2. mode 1
1 |
mysql> truncate table t1; ALTER TABLE t1 AUTO_INCREMENT 101; -- 复原 |
可以看到最终插入的值是一样的,但下一个自增值变成了 105,因为该模式下insert语句处理的时候,提前分配了 4 个自增值,但实际只有了两个。
注:如果你的insert自增列全都有带值,那么处理的时候是不会分配自增值的,经过下面这个实验,可以知道 分配自增值,是在遇到第一个没有带自增列的行时,一次性分配的 :
1 |
-- Tx1,先运行。 -- 插入第2行的时候 sleep 5s |
3. mode 2
1 |
mysql> truncate table t1; ALTER TABLE t1 AUTO_INCREMENT 101; -- 复原 |
结果看起来与 连续模式 一样,其实不然!该模式下,如果另外一个 大块插入 并发执行时,可能会出现以下现象:
- 大块插入的的自增值有间断
- 其它并发执行的事务插入出现 duplicate-key error
1 |
第1点 (create t2 select * from t1) |
总结
上面说了这么多,那么自增模式到底该怎么选择呢?其实很简单,目前数据库默认的 consecutive 即 innodb_autoinc_lock_mode=1 就是最好的模式,一般业务生产库不会有 insert into ... select ...或者 load data infile 这样的维护动作。(提示:即使晚上有数据迁移任务,也不要通过这样的形式进行)
innodb_autoinc_lock_mode=2 可以提高获取表自增id的并发能力(性能),但是除非出现上面演示的 duplicate-key 特殊用法情形,不会像网上所说的获取到相同key导致重复的问题。但是如果binlog在 RBR 格式下不建议使用,可能出现主从数据不一致。还有就是能够容忍gap的存在,以及多个语句insert的自增值交叉。
参考: https://dev.mysql.com/doc/refman/5.6/en/innodb-auto-increment-handling.html
下篇分析遇到过的 MySQL 自增主键相关的具体问题。
转载地址:http://seanlook.com/2017/02/16/mysql-autoincrement/
关于MySQL自增主键的几点问题(上)的更多相关文章
- mybatis的执行流程 #{}和${} Mysql自增主键返回 resultMap 一对多 多对一配置
n Mybatis配置 全局配置文件SqlMapConfig.xml,配置了Mybatis的运行环境等信息. Mapper.xml文件即Sql映射文件,文件中配置了操作数据库的Sql语句.此文件需要在 ...
- mysql自增主键字段重排
不带外键模式的 mysql 自增主键字段重排 1.备份表结构 create table table_bak like table_name; 2.备份表数据 insert into table_bak ...
- Mysql自增主键ID重新排序方法详解
Mysql数据库表的自增主键ID号乱了,需要重新排列. 原理:删除原有的自增ID,重新建立新的自增ID. 1,删除原有主键: ALTER TABLE `table_name` DROP `id`; 2 ...
- 关于mysql自增主键
对于mysql表(其他数据库没测试过) 如果定义了自增主键,并且手动设置了主键的值,那么当再次自增创建数据的时候,回在设置的主键值的基础上进行自增. 如(id是主键): 起始插入(3,1),而后手动插 ...
- mysql自增主键
MariaDB [test]> create table test1(id int primary key auto_increment,name varchar(20))auto_increm ...
- mysql 自增主键为什么不是连续的?
由于自增主键可以让主键索引尽量地保持递增顺序插入,避免了页分裂,因此索引更紧凑 MyISAM 引擎的自增值保存在数据文件中 nnoDB 引擎的自增值,其实是保存在了内存里,并且到了 MySQL 8.0 ...
- 《Mysql - 自增主键为何不是连续的?》
一:自增主键是连续的么? - 自增主键不能保证连续递增. 二:自增值保存在哪里? - 当使用 show create table `table_name`:时,会看到 自增值,也就是 AUTO_INC ...
- mysql自增主键清零方法
MySQL数据库自增主键归零的几种方法 如果曾经的数据都不需要的话,可以直接清空所有数据,并将自增字段恢复从1开始计数: truncate table table_name; 1 当用户没有trunc ...
- Mybatis插入记录并返回MySQL自增主键
mapper Integer insertConfigAndGetId(CrawlerConfig config); xml <insert id="insertConfigAndGe ...
随机推荐
- EasyUI combobox 加载JSON数据
Action返回 JSON 格式如下: jsonResult = { total=7,rows=[ {TEXT=技术支持, ID=402894ca4419acf1014419b148a10000}, ...
- 轻松看懂机器学习十大常用算法 (Machine Learning Top 10 Commonly Used Algorithms)
原文出处: 不会停的蜗牛 通过本篇文章可以对ML的常用算法有个常识性的认识,没有代码,没有复杂的理论推导,就是图解一下,知道这些算法是什么,它们是怎么应用的,例子主要是分类问题. 每个算法都看了 ...
- nyist 20 吝啬的国度(dfs)
吝啬的国度 题目描述: 在一个吝啬的国度里有N个城市,这N个城市间只有N-1条路把这个N个城市连接起来. 现在,Tom在第S号城市,他有张该国地图,他想知道如果自己要去参观第T号城市, 必须经过的前一 ...
- Python语言下图像的操作方法总结
本章主要讲解 图像的读取方式.灰度化操作.图像转化为矩阵的方法 假设 strImgPath是图像的路径, img对象将图片读入到内存中 读取图像的第一种方式:skImage from skimage ...
- windows下编译基于nginx插件的rtmp流媒体服务nginx-rtmp
1 概述 rtmp流媒体服务器,开源方案有多种,包括srs,red5,crtmpserver,fms,nginx插件等.本文描述了基于nginx插件的方式来实现rtmp流媒体服务器nginx-rtmp ...
- TaskScheduler内幕天机解密:Spark shell案例运行日志详解、TaskScheduler和SchedulerBackend、FIFO与FAIR、Task运行时本地性算法详解等
本课主题 通过 Spark-shell 窥探程序运行时的状况 TaskScheduler 与 SchedulerBackend 之间的关系 FIFO 与 FAIR 两种调度模式彻底解密 Task 数据 ...
- asp.net c# 断点续传 下载 Accept-Ranges
转自:http://www.cnblogs.com/90nice/p/3489287.html 1.因为要下载大文件 需要断点续传,使用多线程 分段下载 效率比较高,节省资源. 发点牢骚:下载可以用多 ...
- monodevelop 基础用法
1.mono快捷键 CTRL+K 删除光标所在行的该行后面的代码 CTRL + ALT +C 注释/不注释该行 CTRL+ DOWN 像鼠标滚轮一样向下拖 CTRL + UP 像鼠标滚 ...
- MySQL闪回-binlog2sql
功能 提取SQL 生成回滚SQL 限制: mysql server必须开启,离线模式下不能解析binlog. binlog格式必须是row模式. flashback模式只支持DML,DDL将不 ...
- EOF及相关函数
结论:EOF是在头文件stdio.h中预定义的一个宏,而eof(end of file)是一个与标准输入/输出流相关联的标志位.当文件指针已经指向文件尾且再次尝试读取时,eof标志会被设置.同时,某些 ...