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语句来重置自增主键的值,也可以避免手动指定主键值来避免主键冲突。在实际应用中,根据具体需求选择合适的解决办法。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

福建科立讯通信 指挥调度管理平台 多处文件上传漏洞复现

0x01 产品简介 福建科立讯通信指挥调度管理平台是一个专门针对通信行业的管理平台。该产品旨在提供高效的指挥调度和管理解决方案,以帮助通信运营商或相关机构实现更好的运营效率和服务质量。该平台提供强大的指挥调度功能,可以实时监控和管理通信网络设备、维护人员和工作任…

逻辑回归简单案例分析--鸢尾花数据集

文章目录 1. IRIS数据集介绍2. 具体步骤2.1 手动将数据转化为numpy矩阵2.1.1 从csv文件数据构建Numpy数据2.1.2 模型的搭建与训练2.1.3 分类器评估2.1.4 分类器的分类报告总结2.1.5 用交叉验证(Cross Validation)来验证分类器性能2.1.6 完整代码&#xf…

Kotlin/JS工程构建及编译运行到浏览器

概述 Kotlin/JS 提供了转换 Kotlin 代码、Kotlin 标准库的能力,并且兼容 JavaScript 的任何依赖项。Kotlin/JS 的当前实现以 ES5 为目标。 使用 Kotlin/JS 的推荐方法是通过 kotlin.multiplatform Gradle 插件。它提供了一种集中且便捷的方式来设置与控制以 JavaS…

激光焊接机:塑料产品制造中的革命性优势

随着科技的飞速发展,激光焊接机在塑料产品制造领域的应用越来越广泛。相较于传统的焊接技术,激光焊接机在塑料产品制造中展现出诸多优势,成为现代工业生产中不可或缺的一部分。 一、精确、高效的焊接性能 激光焊接机采用高能激光束作为焊接热…

【React系列】高阶组件

本文来自#React系列教程:https://mp.weixin.qq.com/mp/appmsgalbum?__bizMzg5MDAzNzkwNA&actiongetalbum&album_id1566025152667107329) 一. 高阶组件 1.1. 认识高阶组件 什么是高阶组件呢?相信很多同学都听说过,也用过 高阶函数&…

kafka消息队列安装以及整合springboot使用

文章目录 一、JMS与AMQP二、安装2.1 Java安装2.2 Zookeeper 和 kafka安装2.3 docker-compose 安装【待定,远程连接可能连接不上】 三、Kafka数据存储流程和原理概述和LEOHW讲解四、代码客户端连接kafka五、**ProducerRecord和key的作用**5.1 **如果保证顺序消费&…

C++使用map插入insert数据(二进制数据和非二进制数据)接口封装+读取文件

1、定义编写代码 //生成insert sql语句std::string GetInsertsql(XDATA kv, std::string table);//插入非二进制数据bool Insert(XDATA kv, std::string table);//插入二进制数据bool InsertBin(XDATA kv, std::string table); std::string LXMysql::GetInsertsql(XDATA kv, s…

【四】CocosCreator-修改引擎源码

看源码的过程中,少不了得修修改改源码,做点实验。果断去找找源码在哪里,然后就可以快乐动手改源码了。 CocosCreator引擎源码主要就是js和原生C两类,分别在引擎目录的resources/engine和resources/cocos2d-x下,如图&am…

5600U PVE安装WIN10后直通核显

修改PVE系统配置 请先安装相同版本的PVE系统,其他版本如果存在问题请自行查找。 安装过程比较简单,具体方法请自行百度 1. 修改grub启动参数: 修改文件 /etc/default/grub 中 GRUB_CMDLINE_LINUX_DEFAULT 配置: GRUB_CMDLINE_LI…

自制Java镜像发布到dockerhub公网使用

文章目录 问题现象解决制作Java镜像发布使用 问题现象 书接上回,上周处理了一个docker问题,写了篇博客:自定义docker镜像,ubuntu安装命令并导出我们使用谷歌的jib插件打包,详情可以参考这篇文章:Spring Bo…

【React系列】Redux(一)管理状态

本文来自#React系列教程:https://mp.weixin.qq.com/mp/appmsgalbum?__bizMzg5MDAzNzkwNA&actiongetalbum&album_id1566025152667107329) 在React的开发过程中,Redux对于我们是非常重要的。 但是对于很多人来说,初次接触redux会感觉r…

Gin 框架介绍与快速入门

Gin 框架介绍与快速入门 文章目录 Gin 框架介绍与快速入门一、Gin框架介绍1. 快速和轻量级2. 路由和中间件3. JSON解析4. 支持插件5. Gin相关文档 二、基本使用1.安装2.导入3.第一个Gin 应用 三、应用举例四、Gin 入门核心1.gin.Engine2.gin.Context 一、Gin框架介绍 Gin是一个…

【LMM 009】MiniGPT-4:使用 Vicuna 增强视觉语言理解能力的多模态大模型

论文描述:MiniGPT-4: Enhancing Vision-Language Understanding with Advanced Large Language Models 论文作者:Deyao Zhu∗ Jun Chen∗ Xiaoqian Shen Xiang Li Mohamed Elhoseiny 作者单位:King Abdullah University of Science and Techn…

机器人制作开源方案 | 清洁机器人

作者:胡志宇、白永康、颉志国、刘昭迅、王维浩 单位:北京石油化工学院 指导老师:陈亚、王殿军 1. 设计方案论证 1.1 清洁机器人方案选择 目前,市场上清洁机器人比比皆是,各大品牌之间的竞争也相当激烈,…

【KingbaseES】实现MySql函数Field

CREATE OR REPLACE FUNCTION field(value TEXT, VARIADIC arr TEXT[]) RETURNS INT AS $$ DECLAREi INT; BEGINFOR i IN 1 .. array_length(arr, 1) LOOPIF arr[i] value THENRETURN i;END IF;END LOOP;RETURN 0; END; $$ LANGUAGE plpgsql IMMUTABLE;

Qt6学习笔记:对象树

使用QObject及其子类创建的对象是以对象树的形式来组织的。创建一个QObject对象时若设置一个父对象,它就会被添加到父对象的子对象列表里,一个父对象被删除时,其全部的子对象都会自动删除。QObject类的构造函数里有一个参数用于设置对象的父对…

报表控件Stimulsoft 2023回顾:都做了哪些产品的改变?

在2023年过去一年中,报表控件Stimulsoft 针各类控件都做了重大改变,其中新增了某些产品、同时加强了很多产品的性能和UI设计,更加符合开发者需求,下面就跟随小编一起来回顾,具体都有哪些↓↓↓ Stimulsoft Ultimate &…

HTTP打怪升级之路

新手村 上个世纪80年代末,有一天,Tim Berners-Lee正在工作,他需要与另一台计算机上的同事共享一个文件。他尝试使用电子邮件,但发现电子邮件不能发送二进制文件。Tim Berners-Lee意识到,他需要一种新的协议来共享二进制…

Elasticsearch:Serarch tutorial - 使用 Python 进行搜索 (二)

这个是继上一篇文章 “Elasticsearch:Serarch tutorial - 使用 Python 进行搜索 (一)” 的续篇。在今天的文章中,我们接着来完成如何进行分页及过滤。 分页 - pagination 应用程序处理大量结果通常是不切实际的。 因此&#xff0…

【萤火虫系列教程】1/5-Adobe Firefly 注册账号

001-Adobe Firefly 注册账号 AI时代如火如荼,Adobe也不甘落后,于今年3月份发布AI创意生成工具Firefly(中文翻译:萤火虫) Adobe Firefly简介 Adobe Firefly的官方介绍为:Firefly是Adobe产品中新的创意生成…