前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >自增主键不连续的几种情况

自增主键不连续的几种情况

作者头像
AsiaYe
发布于 2020-05-27 09:35:42
发布于 2020-05-27 09:35:42
3.6K00
代码可运行
举报
文章被收录于专栏:DBA随笔DBA随笔
运行总次数:0
代码可运行

//自增主键不连续的几种情况//

最近在极客时间上学习丁奇大佬的《MySQL 45讲》,这里结合自己的理解分享出来,喜欢的同学可以购买原版课程进行学习,里面的内容很丰富。

00

自增主键介绍

MySQL中的自增主键想必大家都不陌生,它是通过两个参数来控制的,分别是auto_increment_offset和auto_increment_increment,其中offset代表的是自增主键的开始值,而increment代表的是自增主键每次的增长值。常见的设置,是将这两个参数都设置为1。可以使用show create table语法来查看当前表的自增值。

自增值得生成规则如下:

如果即将插入的值>=当前自增值,那么新的自增值等于当前即将插入的值+1,否则自增值不变。

有的时候,我们会发现自增值

01

唯一键冲突导致的自增键修改

当我们给表里面插入数据的时候,如果没有指定主键的值,自增长的属性会自动为我们定义主键的值,如下示例:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
mysql> CREATE TABLE `t` (
    ->   `id` int(11) NOT NULL AUTO_INCREMENT,
    ->   `c` int(11) DEFAULT NULL,
    ->   `d` int(11) DEFAULT NULL,
    ->   PRIMARY KEY (`id`),
    ->   UNIQUE KEY `c` (`c`)
    -> ) ENGINE=InnoDB;
Query OK, 0 rows affected (0.01 sec)
#创建一个表t,其中主键是id,还有唯一键c#先插入一条数据,此时表中的数据是1,1,1。mysql> insert into t values(null, 1, 1);
Query OK, 1 row affected (0.01 sec)

#再插入数据,发现唯一键冲突,自增值已经发生了改变mysql> insert into t values(null, 1, 1);
ERROR 1062 (23000): Duplicate entry '1' for key 'c'
#此时表的自增值已经变为3
mysql> show create table t\G*************************** 1. row ***************************
       Table: t
Create Table: CREATE TABLE `t` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `c` int(11) DEFAULT NULL,
  `d` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `c` (`c`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8
1 row in set (0.00 sec)
#此时再重新插入null,2,2这条记录,发现结果跟我们想象的不一样
mysql> insert into t values(null, 2,2);
Query OK, 1 row affected (0.00 sec)

mysql> select *from t;
+----+------+------+
| id | c    | d    |
+----+------+------+
|  1 |    1 |    1 |
|  3 |    2 |    2 |
+----+------+------+
2 rows in set (0.00 sec)

上述案例中,虽然第二次插入(null,1,1)失败,但是其实已经浪费了一个主键值了。再次插入(null,2,2)结果就不是我们想象的(2,2,2)了,而是(3,2,2)。

02

事务回滚导致的自增键不连续

当我们使用回滚事务的时候,如果该事务内部使用了自增值,那么同样会导致表自增主键出问题,示例如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
mysql> insert into t values (null,1,1);
Query OK, 1 row affected (0.01 sec)

mysql> show create table t\G
*************************** 1. row ***************************
       Table: t
Create Table: CREATE TABLE `t` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `c` int(11) DEFAULT NULL,
  `d` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `c` (`c`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8
1 row in set (0.00 sec)

mysql> begin;
Query OK, 0 rows affected (0.00 sec)

mysql> insert into t values (null,2,2);
Query OK, 1 row affected (0.00 sec)

mysql> rollback;
Query OK, 0 rows affected (0.00 sec)

mysql> insert into t values (null,2,2);
Query OK, 1 row affected (0.00 sec)

mysql> select * from t;
+----+------+------+
| id | c    | d    |
+----+------+------+
|  1 |    1 |    1 |
|  3 |    2 |    2 |
+----+------+------+
2 rows in set (0.00 sec)

那么为什么MySQL之所以在rollback模式下,不回滚自增主键呢?

其实这个问题的本质是为了避免其他并发事务报主键冲突的错误。假设会话A申请到的自增值是2,会话A提交前,会话B申请了自增值3,并提交了事务。如果会话A可以将自增属性回滚,那么其他事务就会接着从2开始申请自增值,而主键等于3的记录已经存在,那么必然会报错。

如果要解决这个问题,就只能在每次申请自增值之前,查询表里面的数据,然后分配自增值,这样会严重影响MySQL的性能。

03

MySQL自增锁优化带来的不连续

在MySQL5.7中,参数innodb_autoinc_lock_mode被用来控制自增锁的模式,该参数可以设置为三个值:0、1、2.

a、当该值为0的时候,是等insert语句执行完成之后才释放自增锁;

b、当该值为1的时候,普通的insert语句,自增锁在申请之后马上释放,insert into select语句,自增锁还是要等语句结束之后才释放

c、该值为2的时候,所有的申请自增主键的动作都是申请完成之后就释放锁

之所以对insert into select语句单独处理,是因为这种语句"预先不知道要申请多少个id",如果我们要select的表有10w行记录,那么要做10w次的申请自增id的动作。MySQL认为这是欠妥当的,因此,对这种批量insert语句,包括load data等,它在内部做了一个自增值生成策略的优化:

1、批量执行的insert语句,第一次申请1个自增id

2、一个id用完了,第二次申请2个自增id

3、2个id用完了,第三次申请4个自增id

例如,我们看下面这个例子:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
mysql> truncate table t;
Query OK, 0 rows affected (0.01 sec)

mysql> insert into t values(null, 1,1);
Query OK, 1 row affected (0.00 sec)

mysql> insert into t values(null, 2,2);
Query OK, 1 row affected (0.00 sec)

mysql> insert into t values(null, 3,3);
Query OK, 1 row affected (0.00 sec)

mysql> insert into t values(null, 4,4);
Query OK, 1 row affected (0.00 sec)

mysql> create table t2 like t;
Query OK, 0 rows affected (0.01 sec)

mysql> insert into t2(c,d) select c,d from t;
Query OK, 4 rows affected (0.00 sec)
Records: 4  Duplicates: 0  Warnings: 0

mysql> insert into t2 values(null, 5,5);
Query OK, 1 row affected (0.01 sec)

mysql> select * from t2;
+----+------+------+
| id | c    | d    |
+----+------+------+
|  1 |    1 |    1 |
|  2 |    2 |    2 |
|  3 |    3 |    3 |
|  4 |    4 |    4 |
|  8 |    5 |    5 |
+----+------+------+
5 rows in set (0.00 sec)

这种模式下,即使我们认为自己插入了正确的自增值,但是结果还是出人意料。实际上,因为t2中要插入4条语句,所以自增id的分配策略是(1),(2,3),(4,5,6,7),因为表t中只有4条记录,所以自增id 5、6、7就被浪费掉了,最后t2中的自增值就从8开始了。

为了避免自增id不连续而造成的主从数据不一致,线上环境,建议设置成innodb_autoinc_lock_mode=2 ,并且 binlog_format=row.这样做,既能提升并发性,又不会出现数据一致性问题.

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-05-24,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 DBA随笔 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
​MySQL引发的监控问题
今天在线上发现一个问题,挺有意思的,这里简单记录下。 问题的背景是查看一个Grafana监控的时候,发现监控没有数据了:
AsiaYe
2021/07/14
9730
​MySQL引发的监控问题
线上MySQL的自增id用尽怎么办?
MySQL的自增id都定义了初始值,然后不断加步长。虽然自然数没有上限,但定义了表示这个数的字节长度,计算机存储就有上限。比如,无符号整型(unsigned int)是4个字节,上限就是2^32 - 1。那自增id用完,会怎么样?
JavaEdge
2021/10/18
2.2K0
MySQL重大Bug!自增主键竟然不是连续递增
AUTO_INCREMENT=2,表示下一次插入数据时,若需要自动生成自增值,会生成id=2。
JavaEdge
2021/10/18
2.8K0
为何要设置sql_require_primary_key参数?
MySQL建表可能会提醒要求表带主键,该操作其实是受到了sql_require_primary_key参数的控制,虽然从范式角度,表应该带主键,但是没太理解它的必要性,技术社群的这篇文章《故障分析 | 为什么 MySQL 8.0.13 要引入新参数 sql_require_primary_key?》给了我们答案,主从复制的场景需要建主键,避免出现主从数据不一致。
bisal
2025/02/06
1160
为何要设置sql_require_primary_key参数?
Mysql系列 - 第4天:DDL常见操作汇总
DDL:Data Define Language数据定义语言,主要用来对数据库、表进行一些管理操作。
路人甲Java
2019/09/16
1K0
MySQL自增列的重复值问题(r12笔记第25天)
如果需要把一台MySQL中的数据定期归档到另外一台MySQL历史库中,那么很可能会发现会有重复值的问题,导致数据导入会失败,而这个问题其实是和自增列的重复值有关,我们来简单看看。 这方面丁奇大师也做了很多详细的说明,还定制了参数,具体可以参见 http://www.csdn.net/article/2015-01-16/2823591 我们来看看这个问题,由此做一个简单的总结。 我们创建一个表t1,指定存储引擎为InnoDB use test; [test]> drop table
jeanron100
2018/03/21
1.4K0
自增长列的一个小问题
之前的文章中写了一个小问题,当我们使用自增长的方式限定了一个自增长字段id以后,如果删除id=7的一条记录,再重新插入新纪录的时候,这个新纪录的id值会是多少?
AsiaYe
2019/11/06
9170
MySQL replace into导致的自增id问题
今天线上遇到一个问题,挺有意思,这里记录一下希望对大家有所帮助。某个表中,只有一条记录,发生高可用切换之后,自增id的值发生了变化,主从的自增id值不一致,导致数据写入报主键冲突的错误。
AsiaYe
2020/07/28
7.7K0
MySQL 8 新特性之自增主键的持久化
自增主键没有持久化是个比较早的bug,这点从其在官方bug网站的id号也可看出(https://bugs.MySQL.com/bug.php?id=199)。由Peter Zaitsev(现Perco
星哥玩云
2022/08/16
6750
MySQL自增列主从不一致的测试(r12笔记第37天)
MySQL里面有一个问题尤其值得注意,那就是自增列的重复值问题,之前也简单分析过一篇MySQL自增列的重复值问题(r12笔记第25天),但是在后续我想了下,还有很多地方需要解释,一个就是从库的自增列是如何维护的,是否重启从库,自增列会受到影响。 我们继续来测试一下。首先复现这个问题。 创建表t1,插入3行数据。 use test; [test]> drop table if exists t1; Query OK, 0 rows affected, 1 warning (0.01 sec
jeanron100
2018/03/21
1.1K0
MySQL自增主键详解「建议收藏」
2.InnoDB引擎的自增值,在MySQL5.7及之前的版本,自增值保存在内存里,并没有持久化。每次重启后,第一次打开表的时候,都会去找自增值的最大值max(id),然后将max(id)+步长作为这个表当前的自增值
全栈程序员站长
2022/09/06
6.3K0
MySQL自增主键详解「建议收藏」
故障分析 | 为什么 MySQL 8.0.13 要引入新参数 sql_require_primary_key?
客户在测试环境给一批无主键表新增自增主键,发现个别表在新增自增主键后出现了主从数据不一致。
爱可生开源社区
2025/01/22
1280
故障分析 | 为什么 MySQL 8.0.13 要引入新参数  sql_require_primary_key?
第17期:索引设计(主键设计)
表的主键指的针对一张表中的一列或者多列,其结果必须能标识表中每行记录的唯一性。InnoDB 表是索引组织表,主键既是数据也是索引。
爱可生开源社区
2020/12/14
6590
新特性解读 | MySQL 8.0 GIPK 不可见主键
作为 MySQL DBA ,相信大家都经历过在复制模式下,如果没有主键,遇到 load data ,大事务,ddl 等有大量表数据行扫描的行为时,会带来严重的主从延迟,给数据库稳定性和数据一致性带来隐患。
爱可生开源社区
2022/08/02
5620
浅析MySQL存储引擎序列属性
墨墨导读:为了达到标识的目的,许多应用程序需要生成唯一编号,比如:商品编号、交易流水号等。MySQL数据库同样能够支持这样的需求场景,AUTO_INCREMENT就是为MySQL实现序列的方式,它会自动生成序列编号。
数据和云
2020/09/14
1.6K0
【MySql】表的约束
真正约束字段的是数据类型,但是数据类型约束很单一,需要有一些额外的约束,更好的保证数据的合法性,从业务逻辑角度保证数据的正确性。比如有一个字段是email,要求是唯一的。表中一定要有各种约束,通过约束,让我们未来插入数据库表中的数据是符合预期的。约束的本质是通过技术收到逼迫程序员插入正确的数据,反过来,站在mysql的视角,凡是插入进来的数据,都是符合数据约束的。约束的最终目标:保证数据的完整性和可预期性所以需要更多的约束。 表的约束很多,这里主要介绍如下几个: null/not null,default, comment, zerofill,primarykey,auto_increment,unique key 。
平凡的人1
2023/10/15
5200
【MySql】表的约束
再谈表的约束
auto_increment:当对应的字段,不给值,会自动的被系统触发,系统会从当前字段中已经有的最大值+1操作,得到一个新的不同的值。通常和主键搭配使用,作为逻辑主键。 自增长的特点:
南桥
2024/08/15
1250
再谈表的约束
OB 运维 | MySQL 迁移 Oracle 场景中自增主键的实践
作者:赵黎明,爱可生 MySQL DBA 团队成员,熟悉 Oracle、MySQL 等数据库,擅长数据库性能问题诊断、事务与锁问题的分析等,负责处理客户 MySQL 及我司自研 DMP 平台日常运维中的问题,对开源数据库相关技术非常感兴趣。
爱可生开源社区
2023/10/23
4490
OB 运维 | MySQL 迁移 Oracle 场景中自增主键的实践
MySQL实战第三十九讲-自增主键为什么不是连续的?
在 第4篇 文章中,我们提到过自增主键,由于自增主键可以让主键索引尽量地保持递增顺序插入,避免了页分裂,因此索引更紧凑。
越陌度阡
2022/11/27
1K0
MySQL实战第三十九讲-自增主键为什么不是连续的?
MySQL 案例:自增列的空洞问题与“小”BUG
在 MySQL 的常见规范里面,每个表都要设置主键,一般来说都会推荐自增列作为主键,这和 MySQL 属于聚簇索引表有关,顺序增长的主键比较合适。而自增列中比较常遇见的问题就是自增列的空洞。原生的 MySQL 自增列也存在一个 BUG,可能会影响到数据一致性,本文也会详细介绍,在自建 MySQL 的时候尽量不要踩到这个坑。
王文安@DBA
2020/08/19
2.2K0
MySQL 案例:自增列的空洞问题与“小”BUG
推荐阅读
相关推荐
​MySQL引发的监控问题
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验