MySQL自增列(AUTO_INCREMENT)相关知识点总结
MySQL的自增列(AUTO_INCREMENT)和其它数据库的自增列对比,有很多特性和不同点(甚至不同存储引擎、不同版本也有一些不同的特性),让人感觉有点稍微复杂。下面我们从一些测试开始,来认识、了解一下这方面的特殊知识点:
自增列持久化问题
如果一个表拥有自增列,当前最大自增列值为9, 删除了自增列6、7、8、9的记录,重启MySQL服务后,再往表里面插入数据,自增列的值为6还是10呢? 如果表的存储引擎为MyISAM呢,又会是什么情况? 下面实验环境为MySQL 5.7.21
mysql> drop table if exists test;
Query OK, 0 rows affected (0.08 sec)
mysql> create table test(id int auto_increment primary key, name varchar(32)) ENGINE=InnoDB;
Query OK, 0 rows affected (0.02 sec)
mysql> insert into test(name)
-> select 'kkk1' from dual union all
-> select 'kkk2' from dual union all
-> select 'kkk3' from dual union all
-> select 'kkk4' from dual union all
-> select 'kkk5' from dual union all
-> select 'kkk6' from dual union all
-> select 'kkk7' from dual union all
-> select 'kkk8' from dual union all
-> select 'kkk9' from dual;
Query OK, 9 rows affected (0.01 sec)
Records: 9 Duplicates: 0 Warnings: 0
mysql> select * from test;
+----+------+
| id | name |
+----+------+
| 1 | kkk1 |
| 2 | kkk2 |
| 3 | kkk3 |
| 4 | kkk4 |
| 5 | kkk5 |
| 6 | kkk6 |
| 7 | kkk7 |
| 8 | kkk8 |
| 9 | kkk9 |
+----+------+
9 rows in set (0.00 sec)
mysql> delete from test where id>=6;
Query OK, 4 rows affected (0.00 sec)
重启MySQL服务后,然后我们插入一条记录,字段ID会从什么值开始呢? 如下所示,如果表的存储引擎为InnoDB,那么插入的数据的自增字段值为6.

接下来,我们创建一个MyISAM类型的测试表。如下所示:
mysql> drop table if exists test;
Query OK, 0 rows affected (0.01 sec)
mysql> create table test(id int auto_increment primary key, name varchar(32)) engine=MyISAM;
Query OK, 0 rows affected (0.02 sec)
mysql>
insert into test(name)
select 'kkk1' from dual union all
select 'kkk2' from dual union all
select 'kkk3' from dual union all
select 'kkk4' from dual union all
select 'kkk5' from dual union all
select 'kkk6' from dual union all
select 'kkk7' from dual union all
select 'kkk8' from dual union all
select 'kkk9' from dual;
mysql> delete from test where id>=6;
Query OK, 4 rows affected (0.00 sec)
删除了id>=6的记录后,重启MySQL服务,如下所示,测试结果为id =10, 那么为什么出现不同的两个结果呢?这个是因为InnoDB存储引擎中,自增主键没有持久化,而是放在内存中,关于自增主键的分配,是由InnoDB数据字典内部一个计数器来决定的,而该计数器只在内存中维护,并不会持久化到磁盘中。当数据库重启时,该计数器会通过SELECT MAX(ID) FROM TEST FOR UPDATE这样的SQL语句来初始化(不同表对应不同的SQL语句), 其实这是一个bug来着, 对应的链接地址为:https://bugs.mysql.com/bug.php?id=199,直到MySQL 8.0 ,才将自增主键的计数器持久化到redo log中。每次计数器发生改变,都会将其写入到redo log中。如果数据库发生重启,InnoDB会根据redo log中的计数器信息来初始化其内存值。 而对应与MySIAM存储引擎,自增主键的最大值存放在数据文件当中,每次重启MySQL服务都不会影响其值变化。

自增列细节特性
1:SQL模式的NO_AUTO_VALUE_ON_ZERO值影响AUTO_INCREMENT列的行为。
mysql> drop table if exists test;
Query OK, 0 rows affected (0.01 sec)
mysql> create table test(id int auto_increment primary key, name varchar(32));
Query OK, 0 rows affected (0.02 sec)
mysql> select @@sql_mode;
+-------------------------------------------------------------------------------------------------------------------------------------------+
| @@sql_mode |
+-------------------------------------------------------------------------------------------------------------------------------------------+
| ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION |
+-------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)
mysql> insert into test(id, name) value(0, 'kerry');
Query OK, 1 row affected (0.00 sec)
mysql> select * from test;
+----+-------+
| id | name |
+----+-------+
| 1 | kerry |
+----+-------+
1 row in set (0.00 sec)
mysql>
如上所示,如果在SQL模式里面没有设置NO_AUTO_VALUE_ON_ZERO的话,那么在默认设置下,自增列默认一般从1开始自增,插入0或者null代表生成下一个自增长值。如果用户希望插入的值为0,而该列又是自增长的,那么这个选项就必须设置
mysql> SET SQL_MODE="NO_AUTO_VALUE_ON_ZERO,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION";
Query OK, 0 rows affected (0.00 sec)
mysql> insert into test(id, name) value(0, 'kerry');
Query OK, 1 row affected (0.01 sec)
mysql> select * from test;
+----+-------+
| id | name |
+----+-------+
| 0 | kerry |
| 1 | kerry |
+----+-------+
2 rows in set (0.00 sec)
mysql>
2:如果把一个NULL值插入到一个AUTO_INCREMENT数据列里去,MySQL将自动生成下一个序列编号。如下所示,这个语法对于熟悉SQL Server中自增字段的人来来看,简直就是不可思议的事情。
mysql> drop table if exists test;
Query OK, 0 rows affected (0.03 sec)
mysql> create table test(id int auto_increment primary key, name varchar(32));
Query OK, 0 rows affected (0.05 sec)
mysql> insert into test(id , name) value(null, 'kerry');
Query OK, 1 row affected (0.00 sec)
mysql> select * from test;
+----+-------+
| id | name |
+----+-------+
| 1 | kerry |
+----+-------+
1 row in set (0.00 sec)
3:获取当前自增列的值
获取当前自增列的值,可以使用 LAST_INSERT_ID函数,注意,这个是一个系统函数,可获得自增列自动生成的最后一个值。但该函数只与服务器的本次会话过程中生成的值有关。如果在与服务器的本次会话中尚未生成AUTO_INCREMENT值,则该函数返回0
mysql> select last_insert_id();
+------------------+
| last_insert_id() |
+------------------+
| 1 |
+------------------+
1 row in set (0.00 sec)
mysql> insert into test(name) value('jimmy');
Query OK, 1 row affected (0.00 sec)
mysql> select last_insert_id();
+------------------+
| last_insert_id() |
+------------------+
| 2 |
+------------------+
1 row in set (0.00 sec)
mysql> select * from test;
+----+-------+
| id | name |
+----+-------+
| 1 | kerry |
| 2 | jimmy |
+----+-------+
2 rows in set (0.00 sec)
如果要获取自增列的下一个值,那么可以使用show create table tablename查看。如下截图所示

4:自增列跳号
MySQL中,自增字段可以跳号:可以插入一条指定自增列值的记录(即使插入的值大于自增列的最大值),如下所示,当前自增列最大值为1,我插入一个200的值,然后就会以200为基础继续自增,而且我还可以继续插入ID=100的记录,无需任何额外设置。
mysql> select * from test;
+----+-------+
| id | name |
+----+-------+
| 1 | kerry |
+----+-------+
1 row in set (0.00 sec)
mysql> insert into test value(200, 'test');
Query OK, 1 row affected (0.01 sec)
mysql> select * from test;
+-----+-------+
| id | name |
+-----+-------+
| 1 | kerry |
| 200 | test |
+-----+-------+
2 rows in set (0.00 sec)
mysql> insert into test(name) value('test2');
Query OK, 1 row affected (0.01 sec)
mysql> select * from test;
+-----+-------+
| id | name |
+-----+-------+
| 1 | kerry |
| 200 | test |
| 201 | test2 |
+-----+-------+
3 rows in set (0.00 sec)
mysql>
mysql> insert into test(id, name) value(100, 'ken');
Query OK, 1 row affected (0.01 sec)
mysql> select * from test;
+-----+-------+
| id | name |
+-----+-------+
| 1 | kerry |
| 100 | ken |
| 200 | test |
| 201 | test2 |
+-----+-------+
4 rows in set (0.00 sec)
另外一个是关于自增列逻辑跳号问题,在一个事务里面,使用遇到事务回滚,自增列就会跳号,如下所示,id从201 跳到 203了。
mysql> begin;
Query OK, 0 rows affected (0.00 sec)
mysql> insert into test(name) value('kkk');
Query OK, 1 row affected (0.00 sec)
mysql> select * from test;
+-----+-------+
| id | name |
+-----+-------+
| 1 | kerry |
| 100 | ken |
| 200 | test |
| 201 | test2 |
| 202 | kkk |
+-----+-------+
5 rows in set (0.00 sec)
mysql> rollback;
Query OK, 0 rows affected (0.00 sec)
mysql> insert into test(name) value('kkk');
Query OK, 1 row affected (0.00 sec)
mysql> select * from test;
+-----+-------+
| id | name |
+-----+-------+
| 1 | kerry |
| 100 | ken |
| 200 | test |
| 201 | test2 |
| 203 | kkk |
+-----+-------+
5 rows in set (0.00 sec)
当然,无论MySQL还是其他关系型数据库,都会遇到这种逻辑跳号的情况,例如ORACLE的序列也会存在这种逻辑跳号问题。为提高自增列的生成效率,都将生成自增值的操作设计为非事务性操作,表现为当事务回滚时,事务中生成的自增值不会被回滚。
5:truncate table操作会引起自增列从头开始计数
mysql> truncate table test;
Query OK, 0 rows affected (0.01 sec)
mysql> insert into test(name) value('kerry');
Query OK, 1 row affected (0.00 sec)
mysql> select * from test;
+----+-------+
| id | name |
+----+-------+
| 1 | kerry |
+----+-------+
1 row in set (0.00 sec)
mysql>
6:修改AUTO_INCREMENT的值来修改自增起始值。
mysql> select * from test;
+----+-------+
| id | name |
+----+-------+
| 1 | kerry |
+----+-------+
1 row in set (0.00 sec)
mysql> alter table test auto_increment=100;
Query OK, 0 rows affected (0.00 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> insert into test(name) value('k3');
Query OK, 1 row affected (0.00 sec)
mysql> select * from test;
+-----+-------+
| id | name |
+-----+-------+
| 1 | kerry |
| 100 | k3 |
+-----+-------+
2 rows in set (0.00 sec)
当然MySQL还有一些相关知识点,这里没有做总结,主要是没有遇到过相关场景。以后遇到了再做总结,另外一方面,写技术文章,很难面面俱到,这样太耗时也太累人了!
参考资料:
http://www.cnblogs.com/TeyGao/p/9279390.html
https://dev.mysql.com/doc/refman/5.7/en/example-auto-increment.html
https://dev.mysql.com/doc/refman/5.7/en/innodb-auto-increment-handling.html
http://www.cnblogs.com/yangzumin/p/3756583.html
MySQL自增列(AUTO_INCREMENT)相关知识点总结的更多相关文章
- 怎么重置mysql的自增列AUTO_INCREMENT初时值
重置 MySQL 自增列 AUTO_INCREMENT 初时值 注意, 使用以下任意方法都会将现有数据删除. 方法一: delete from tb1; ALTER TABLE tbl AUTO_IN ...
- (转)mysql自增列导致主键重复问题分析
mysql自增列导致主键重复问题分析... 原文:http://www.cnblogs.com/cchust/p/3914935.html 前几天开发童鞋反馈一个利用load data infile ...
- mysql自增列导致主键重复问题分析。。。
前几天开发童鞋反馈一个利用load data infile命令导入数据主键冲突的问题,分析后确定这个问题可能是mysql的一个bug,这里提出来给大家分享下.以免以后有童鞋遇到类似问题百思不得其解,难 ...
- Mysql 自增列 主键
Mysql中假如有 ID Int auto_increment, CID varchar(36). 通常情况下都是 ID设置为主键. 假如要设置CID为主键.自增列ID必需是唯一索引. create ...
- MySQL自增列锁模式 innodb_autoinc_lock_mode不同参数下性能测试
对于innodb_autoinc_lock_mode 各种参数的值的含义,网上也有各种详解,看完觉得意犹未尽,这里不做阐述,只动手测试,看看性能上,到底有没有理论上所说的差别.对于自增列的锁定,据说是 ...
- 关于sparksql中设置自定义自增列的相关要点(工作共踩过的坑-1)
小白终于进入了职场,从事大数据方面的工作! 分到项目组了,搬砖的时候遇到了一个这样的问题. 要求:用spark实现oracle的存储过程中计算部分. 坑:由于报表中包含了一个ID字段,其要求是不同的区 ...
- mysql自增字段AUTO_INCREMENT重排或归零
由于删除了某些记录行,导致自增字段不连续了,重排或归零的方法: 方法1:truncate table 你的表名//这样不但重新定位自增的字段,而且会将表里的数据全部删除,慎用! 方法2:delete ...
- 重置Mysql自增列的开始序号
ALTER TABLE TableName AUTO_INCREMENT = 5; 代表重新从5开始(包括5)
- 关于MySQL自增主键的几点问题(上)
前段时间遇到一个InnoDB表自增锁导致的问题,最近刚好有一个同行网友也问到自增锁的疑问,所以抽空系统的总结一下,这两个问题下篇会有阐述. 1. 划分三种插入类型 这里区分一下几种插入数据行的类型,便 ...
随机推荐
- shell 问题备忘
一 ls结果赋给变量 dirSrc=$(ls test/ -l | awk '/^d/{print $NF}') echo "dirSrc is $dirSrc" 二 使用cut查 ...
- js中对于逗号的运算符!
先展示一个例子! var f = (function f() { return '1'; } , function g(){ return 1; } )(); console.log(typeof f ...
- PHP的Memcached简单实现
Memcached 是一个高性能的分布式内存对象缓存系统,用于动态Web应用以减轻数据库负载.它通过在内存中缓存数据和对象来减少读取数据库的次数,从而提高动态.数据库驱动网站的速度.也可动态缓存一些实 ...
- HBase篇--HBase常用优化
一.前述 HBase优化能够让我们对调优有一定的理解,当然企业并不是所有的优化全都用,优化还要根据业务具体实施. 二.具体优化 1.表的设计 1.1 预分区 默认情况下,在创建HBase表的时候会自 ...
- Hystrix介绍
Hystrix是什么 在分布式环境中,许多服务依赖项中的一些必然会失败.Hystrix是一个库,通过添加延迟容忍和容错逻辑,帮助你控制这些分布式服务之间的交互.Hystrix通过隔离服务之间的访问点. ...
- solr之环境配置三
配置安装Solr到Tomcat 1. 解压 solr4.7.2.zip 2. 将 solr-4.7.2\dist\solr-4.7.2.war拷贝到 apache-tomcat-7.0.55\weba ...
- AndroidStudio意外崩溃,电脑重启,导致重启Find In Path...查找功能失效,搜索结果缺失
解决方案: <AndroidStudio意外崩溃,电脑重启,导致重启打开Androidstudio后所有的import都出错>
- java中用MessageFormat格式化json字符串用占位符时出现的问题can't parse argument number
在MessageFormat.format方法中组装jason数据字符串:{code:"w1",des:"w2"},起止分别有左大括号和右大括号. 直接写的点位 ...
- 【ASP.NET Core快速入门】(六)配置的热更新、配置的框架设计
配置的热更新 什么是热更新:一般来说,我们创建的项目都无法做到热更新:即项目无需重启,修改配置文件后读取到的信息就是修改配置之后的 我们只需要吧项目中用到的IOptions改成IOptionsSnap ...
- SQL——嵌套查询与子查询
前言 sql的嵌套查询可以说是sql语句中比较复杂的一部分,但是掌握好了的话就可以提高查询效率.下面将介绍带in的子查询.带比较运算符的子查询.带any/all的子查询.带exists的子查询以及基于 ...