MySQL数据库设置主键自增、自增主键为什么不能保证连续递增

文章目录

  • 一、设置主键自增
    • 1.1、建表时设置主键自增
    • 1.2、建表后设置主键自增
    • 1.3、删除自增约束
  • 二、自增列:AUTO_INCREMENT
    • 2.1、自增起始值和自增步长
    • 2.2、自增主键存储策略
    • 2.3、自增值修改机制
    • 2.3、特点和要求
  • 三、自增字段值不连续
    • 3.1、自增不连续的示例
      • 3.1.1、示例一:唯一索引冲突导致自增不连续
      • 3.1.2、示例二:批量插入会造成主键不连续
    • 3.2、mysql自增主键不连续场景
    • 3.3、解决方法
    • 3.4、自增id为什么不回退复用
    • 3.5、总结

以下内容基于MySql8.0进行讲解。

一、设置主键自增

1.1、建表时设置主键自增

建表时设置主键自增并指定自增起始值

CREATE TABLE `user` (`id` int NOT NULL PRIMARY KEY AUTO_INCREMENT,`name` varchar(30) DEFAULT NULL comment '姓名',`age` int DEFAULT NULL comment '年龄',UNIQUE KEY `uniq_name` (`name`)
) ENGINE=InnoDB AUTO_INCREMENT = 100;

说明:

  • AUTO_INCREMENT:指定该列自增
  • AUTO_INCREMENT = 100:指定自增起始值为100,默认起始值为1,每次自增1

在这里插入图片描述

1.2、建表后设置主键自增

如果数据表已经存在,可以使用 ALTER TABLE 语句修改表结构并设置自增主键。例如,将 user表中的 id 字段设置为自增主键,可以使用如下 SQL 语句:

ALTER TABLE user MODIFY id INT AUTO_INCREMENT PRIMARY KEY;

1.3、删除自增约束

将AUTO_INCREMENT 从字段类型中删除即可删除掉自增约束

alter table user MODIFY id INT PRIMARY KEY;

二、自增列:AUTO_INCREMENT

2.1、自增起始值和自增步长

MySQL提供了主键自增机制AUTO_INCREMENT,对主键使用,保证主键的唯一性,默认值起始值为1,每次增长量为1,也就是自增起始值auto_increment_offset 和自增步长 auto_increment_increment。

# 查询mysql是否支持自增长,查询自增长步长,自增长起始值
mysql> show variables like '%auto_increment%';
+-------------------------|-------+
|       Variable_name     | Value |
+-------------------------|-------+
|auto_increment_increment |   1   |
+-------------------------|-------+
|auto_increment_offset    |   1   |
+-------------------------|-------+

说明:

  • auto_increment_increment:自增步长,默认每次自增1。
  • auto_increment_offset:自增起始值,默认从1开始。

在这里插入图片描述

2.2、自增主键存储策略

表的结构定义存放在后缀名为.frm的文件中,但是并不会保存自增值。不同的引擎对于自增值的存储策略不同。

  • MyISAM引擎的自增值保存在数据文件中。
  • InnoDB引擎的自增值是保存在了内存里,到了MySQL 8.0版本后,才有了“自增值持久化”的能力,即才实现了“如果发生重启,表的自增值可以恢复为MySQL重启前的值”,具体情况是:
    • 在MySQL 5.7及之前的版本,自增值保存在内存里,并没有持久化。每次重启后,第一次打开表的时候,都会去找自增值的最大值max(id),然后将max(id)+1作为这个表当前的自增值。 比如一个表当前数据行里最大的id是10,AUTO_INCREMENT=11。这时删除id=10的行,AUTO_INCREMENT还是11。但如果马上重启实例,重启后这个表的AUTO_INCREMENT就会变成10。 也就是说,MySQL重启可能会修改一个表的AUTO_INCREMENT的值。
    • 在MySQL 8.0版本,将自增值的变更记录在了redo log中,重启的时候依靠redo log恢复重启之前的值。

2.3、自增值修改机制

  • 如果自增id插入值为0、null或未指定值,那么就把当前表的自增值AUTO_INCREMENT填到自增字段。
  • 如果插入数据时,id指定了具体的值,如果这个具体值不小于自增值AUTO_INCREMENT,则主键值不变,自增值AUTO_INCREMENT=主键值+步长值;否则,自增值AUTO_INCREMENT不变,不存在主键冲突则插入成功。

2.3、特点和要求

  • 默认情况下,AUTO_INCREMENT 的初始值是 1,每新增一条记录,字段值自动加 1。
  • 一个表中只能有一个字段使用 AUTO_INCREMENT 约束,且该字段必须有唯一索引,以避免序号重复(即为主键或主键的一部分)。
  • AUTO_INCREMENT 约束的字段必须具备 NOT NULL 属性。
  • AUTO_INCREMENT 约束的字段只能是整数类型(TINYINT、SMALLINT、INT、BIGINT 等)。
  • AUTO_INCREMENT 约束字段的最大值受该字段的数据类型约束,如果达到上限,AUTO_INCREMENT 就会失效。例如 INT 类型的最大值为 2147483647。

错误演示

create table employee(eid int auto_increment,ename varchar(20)
);
# ERROR 1075 (42000): Incorrect table definition; there can be only one auto column and it must be defined as a key   
create table employee(eid int primary key,ename varchar(20) unique key auto_increment
);
# ERROR 1063 (42000): Incorrect column specifier for column 'ename'  因为ename不是整数类型

三、自增字段值不连续

在某些情况下自增字段的值的不连续的,如果你不知道有这个问题可能会踩坑。

下面我们通过一个实例分析自增字段的值为什么不连续。

3.1、自增不连续的示例

以下列举几个自增不连续的示例。

3.1.1、示例一:唯一索引冲突导致自增不连续

CREATE TABLE `user` (`id` int NOT NULL PRIMARY KEY AUTO_INCREMENT,`name` varchar(30) DEFAULT NULL comment '姓名',`age` int DEFAULT NULL comment '年龄',UNIQUE KEY `uniq_name` (`name`)
) ENGINE=InnoDB AUTO_INCREMENT = 1;

向user表中插入一条数据,sql如下:

insert into user values(1,'张三',1);

此时,表 user 中已经有了(1,‘张三’,1)这条记录,这时再执行一条插入数据命令:

mysql> INSERT INTO user VALUES(null,'张三',1);
ERROR 1062 (23000): Duplicate entry '张三' for key 'user.uniq_name'

由于表中已经存在 name=1 的记录,所以报 Duplicate key error(唯一键冲突)。在这之后,再插入新的数据时,自增 id 就是 3,这样就出现了自增字段值不连续的情况。
在这里插入图片描述
解释:MySQL对于递增值的使用是一次性的,那么第二次执行插入时,不管语句成功还是失败,那么这个递增值就会浪费掉。在执行insert into user values(null,‘张三’,1);时虽然唯一索引冲突了,但是自增id已经自增了一个,内存中自增id已经用到了2,所以下一次插入时id会再次自增变成了3。

3.1.2、示例二:批量插入会造成主键不连续

为了保证主键id的唯一性,在申请自增id时,MySQL会对申请操作加锁。一般情况下,这个申请动作会很快。

对于一般的批量插入,比如insert into … values(xxx),由于插入的Value个数可以提前计算得出,MySQL会一次性的申请足够数量的id,以保证性能。

但是对于insert into … select 这种语句就有点麻烦了,由于无法确定到底需要申请多个主键id,如果插入一条申请一个的话,假设要插入100万条记录,那就得申请100万次,可想而知性能会有多么差劲。

批量插入数据,包含的语句类型是 insert…select、replace…select 和 load data 语句。

所以对于这种批量插入的语句,MySQL采用了一种翻倍申请的优化策略:

  1. 语句执行过程中,第一次申请自增id,会分配1个;
  2. 1个用完以后,这个语句第二次申请自增id,会分配2个;
  3. 2个用完以后,还是这个语句,第三次申请自增id,会分配4个;
  4. 依次类推,同一个语句去申请自增id,每次会申请到的自增id个数都是上一次的两倍。
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;
insert into t values(null, 1,1);
insert into t values(null, 2,2);
insert into t values(null, 3,3);
insert into t values(null, 4,4);
create table t2 like t;
insert into t2(c,d) select c,d from t;
insert into t2 values(null, 5,5);

在这里插入图片描述
由于这种规则,t2表的批量插入就只用到了 id=1 到 id=4,id=8,而id=5 到id=7就被浪费掉了,因此自增主键的批量申请也会导致自增主键出现不连续的情况。

3.2、mysql自增主键不连续场景

1. 删除记录导致的不连续
当我们删除表中的记录时,MySQL的自增主键并不会重置。下次插入记录时,自增主键会从上一次插入的最大主键值加1开始递增。如果删除了表中的某些记录,那么主键的值就会跳跃,导致不连续。

2. 回滚操作导致的不连续
MySQL的自增主键是基于事务的,如果在事务中插入了记录,但是事务回滚了,那么自增主键的值也会回滚,导致不连续。

3. 唯一键冲突导致的不连续
若有唯一索引冲突,则会导致自增主键的不连续。MySQL对于递增值的使用是一次性的,那么第二次执行插入时,不管语句成功还是失败,那么这个递增值就会浪费掉。

4.MySQL5.7及以前的版本重启导致不连续
MySQL5.7及以前的版本InnoDB存储引擎不支持自增值持久化,重启导致自增值丢失。

5.批量插入会造成主键不连续
为了保证主键id的唯一性,在申请自增id时,MySQL会对申请操作加锁。一般情况下,这个申请动作会很快。

对于一般的批量插入,比如insert into … values(xxx),由于插入的Value个数可以提前计算得出,MySQL会一次性的申请足够数量的id,以保证性能。

但是对于insert into … select 这种语句就有点麻烦了,由于无法确定到底需要申请多个主键id,如果插入一条申请一个的话,假设要插入100万条记录,那就得申请100万次,可想而知性能会有多么差劲。

所以对于这种批量插入的语句,MySQL采用了一种翻倍申请的优化策略:

语句执行时,第一次申请一个自增id,第二次申请2个自增id,第三次申请4个自增id…

即每次申请的数量都比上次多一倍,这样虽然会浪费一些自增id,但是可以保证插入的效率,从性能角度来看,是可以接受的。

3.3、解决方法

1. 使用TRUNCATE TABLE重置自增主键
如果希望将自增主键重置为连续的递增序列,可以使用TRUNCATE TABLE语句删除表中的所有记录,并重置自增主键的值。例如:

TRUNCATE TABLE table_name;

需要注意的是,TRUNCATE TABLE语句会删除表中的所有记录,并且无法回滚。

2. 使用ALTER TABLE重置自增主键
ALTER TABLE语句可以用来修改表的结构,包括重置自增主键。可以使用以下语句将自增主键重置为指定的值:

ALTER TABLE table_name AUTO_INCREMENT = value;

其中,table_name是需要修改的表名,value是希望设置的自增主键值。

3. 避免手动指定主键值
为了避免因手动指定主键值而导致的主键冲突,可以遵循以下原则:

  • 不要在插入记录时手动指定主键值,而是让MySQL自动生成;
  • 如果需要获取自动生成的主键值,可以使用LAST_INSERT_ID()函数。
    例如,插入一条记录并获取自动生成的主键值的示例代码如下:
INSERT INTO table_name (column1, column2) VALUES ('value1', 'value2');
SELECT LAST_INSERT_ID();

3.4、自增id为什么不回退复用

大家可能会有点疑问,为什么自增id是一次性使用的?

其实原因也很简单,大家稍微一想就明白了。

假设有两个事务在同时执行,为了保证自增id的唯一性,MySQL会对申请动作加锁,然后两个事务各获得一个自增id。比如事务1申请到了自增id100,事务2申请到了自增id101。

当事务2成功提交,事务1因为某些原因回滚了。

如果我们要回退复用事务1的id,将AUTO_INCREMENT又设置成了100+1,那么下一个事务来申请自增id时,就会拿到101,而这时101已经被事务2用掉了,就会造成主键冲突。

当然我们也可以每次都让MySQL检查一下主键是否冲突,如果冲突就跳过这个id,但是这样一来,本来申请自增id这个很轻的动作就会变得很重,对性能的影响就会很大。

所以,从性能角度考虑,InnoDB只保证了主键id是大致递增的,而不保证是顺序递增的。

3.5、总结

MySQL的自增主键在特定情况下可能会出现不连续的情况,包括删除记录、回滚操作和主键冲突。为了解决这个问题,我们可以使用TRUNCATE TABLE或ALTER TABLE语句来重置自增主键的值,也可以避免手动指定主键值来避免主键冲突。在实际应用中,根据具体需求选择合适的解决办法。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/599883.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【书生·浦语大模型实战营01】《书生·浦语大模型全链路开源体系》

《书生浦语大模型全链路开源体系》 1. 大模型成为热门关键词 LLM发展时间轴 2. 书生浦语全链条开源开放体系 微调:XTuner 支持全参数微调支持LoRA等低成本微调 评测:OpenCompass 全方位评测,性能可复现80套评测集, 40万道题目…

【金猿CIO展】现代咨询CIO崔恩博:数字化转型,CIO不仅要懂技术和业务,更要“懂人”...

‍ 崔恩博 本文由现代咨询CIO崔恩博撰写并投递参与“数据猿年度金猿策划活动——2023大数据产业年度优秀CIO榜单及奖项”评选。 大数据产业创新服务媒体 ——聚焦数据 改变商业 最近几年,大数据行业的发展备受关注,尤其是2019年以后,随着企业…

73.乐理基础-打拍子-还原号、临时变音记号在简谱中的规则

上一个内容:72.乐理基础-打拍子-加延音线的节奏型-CSDN博客 下图中1-13,就是四几拍中所有可能出现的节奏型,以及它们的组合方式,需要练习,可以买一本视唱书去练习,搜乐谱练习,自己写节奏型根据…

msyql迁移到人大金仓数据库

1,点击数据迁移工具 2,在浏览器上输入http://localhost:54523,默认账号和密码为kingbase,kingbase,进入之后,就是项目的页面 3,数据库管理 添加源数据库,点击确定就可以了,也可以测…

Jmeter接口测试响应数据中文显示为Unicode码的解决方法

问题:使用jmeter测试接口,返回响应数据汉字显示为Unicode 解决结果: 解决过程: 1.修改jmeter配置文件中的默认编码 在Jmeter的安装路径下打开bin文件夹下的jmeter.properties文件,搜索关键词default.encoding定位到语句…

关于MySQL Cluster

目录 1.MySQL Cluster2.MySQL Cluster架构3.MySQL Cluster 与 MySQL 主从架构有什么区别4.参考 MySQL Cluster是MySQL的一个高可用性,高性能的分布式数据库解决方案。它结合了内存数据库和共享无状态架构的技术,提供了99.999%的可用性,满足严…

prometheus grafana linux服务器监控

文章目录 前传node-exporter安装配置promethues监控node节点grafana操作查看监控:外传 前传 prometheus grafana的安装使用:https://nanxiang.blog.csdn.net/article/details/135384541 本文说下监控nginx,prometheus grafana linux 安装配…

悟的排列数

题目: 思路: 在主函数中,程序首先使用 scanf 输入两个整数 n 和 m,表示要计算的排列数。然后,调用递归函数 array 计算排列数,并将结果保存在变量 ret 中。最后,使用 printf 输出计算结果 re…

2024最新Java基础面试题大全(一)

1、String可以被继承&#xff1f; 不能被继承&#xff0c;因为String类有final修饰符&#xff0c;而final修饰的类是不能被继承的。 public final class String implements java.io.Serializable, Comparable<String>, CharSequence {// 省略...  }2、常见集合类 Java…

LabVIEW开发智能水泵监测系统

LabVIEW开发智能水泵监测系统 水泵作为水利、石化、农业等领域的重要设备&#xff0c;其能效与健康状态直接关系到提灌泵站的运行效率。尽管水泵机组在全球能源消耗中占有显著比例&#xff0c;但实际运行效率常因设备老化和维护不当而远低于预期。这一状况需要更高效的监测手段…

调用第三方接口遇到的13大坑

前言 在实际工作中&#xff0c;我们经常需要在项目中调用第三方API接口&#xff0c;获取数据&#xff0c;或者上报数据&#xff0c;进行数据交换和通信。 那么&#xff0c;调用第三方API接口会遇到哪些问题&#xff1f;如何解决这些问题呢&#xff1f; 这篇文章就跟大家一起聊…

C# Onnx Chinese CLIP 通过一句话从图库中搜出来符合要求的图片

目录 效果 生成图片特征 查找踢足球的人 测试图片 模型信息 image_model.onnx text_model.onnx 项目 代码 Form1.cs Clip.cs 下载 C# Onnx Chinese CLIP 通过一句话从图库中搜出来符合要求的图片 效果 生成图片特征 查找踢足球的人 测试图片 模型信息 image_mod…

UOS下通过SSH隧道访问云端内网windows桌面

1 用户痛点 随着时代的发展&#xff0c;众多企业的服务器慢慢走向云端。大量云端服务器节省企业成本的同时&#xff0c;也带来了安全性问题。例如&#xff1a;管理云端的服务器&#xff0c;特别是windows桌面服务器&#xff0c;往往需要给这个服务器分配一个公网IP地址&#x…

利用C#实现贪吃蛇

说明 本文根据B站up主唐老狮的课程所学所记 目录 说明本文根据B站up主唐老狮的课程所学所记 UML面向对象七大原则总体实现目标单一职责原则&#xff08;SRP&#xff0c;Single Responsibility Principle&#xff09;开闭原则&#xff08;OCP&#xff0c;Open-Closed Principle…

Flume基础知识(二):Flume安装部署

1. Flume 安装部署 1.1 安装地址 &#xff08;1&#xff09;Flume 官网地址&#xff1a;Welcome to Apache Flume — Apache Flume &#xff08;2&#xff09;文档查看地址&#xff1a;Flume 1.11.0 User Guide — Apache Flume &#xff08;3&#xff09;下载地址&#xf…

python基础语法(一)变量

目录 使用变量 变量的类型 整数 浮点数 字符串 布尔 其他 使用变量 读取变量的值 a 10 print(a) 修改变量的值 a20 print(a) 在Python中&#xff0c;需要注意的是&#xff0c;修改变量也是使用运算&#xff0c;看起来和定义变量没有明显区别 当然&#xff0c;也可…

qiankun 公共依赖

1、提取公共依赖的目的 减少相同资源的重复加载资源版本不同步打包文件庞大2、如何提取公共依赖 基本思路&#xff1a;1、相同依赖 采用 CDN 的方式加载&#xff0c;并把 所有依赖的 CDN 链接 统一放到一个文件中进行管理 2、把存放 CDN 链接的文件&#xff0c;引入到 vue.conf…

创意与技术的结晶:AI魔法绘图与中文描述的完美结合

在人类文明的长河中&#xff0c;创意与技术一直是推动发展的重要动力。随着科技的日新月异&#xff0c;人工智能&#xff08;AI&#xff09;在创意领域的应用逐渐崭露头角&#xff0c;而AI魔法绘图与中文描述的结合&#xff0c;更是将这一趋势推向了新的高度。AI魔法绘图是一种…

NNote插件:让网络阅读变得更高效,轻松同步至Notion笔记

NNote笔记 在这个互联网时代&#xff0c;我们每天都在浏览器中阅读大量的文章和资讯&#xff0c;时常会遇到让人眼前一亮的观点和想法。然而&#xff0c;当我们试图将这些精彩内容记录下来时&#xff0c;却常常感受到复制粘贴的繁琐。为了解决这个问题&#xff0c;NNote插件应运…

HarmonyOS 应用开发学习笔记 ets自定义组件及其引用 @Component自定义组件

Component注解的作用是用来构建自定义组件 Component组件官方文档 自定义组件具有以下特点&#xff1a; 可组合&#xff1a;允许开发者组合使用系统组件、及其属性和方法。 可重用&#xff1a;自定义组件可以被其他组件重用&#xff0c;并作为不同的实例在不同的父组件或容器…