MySQL的自增列(AUTO_INCREMENT)和其它数据库的自增列对比,有很多特性和不同点(甚至不同存储引擎、不同版本也有一些不同的特性),让人感觉有点稍微复杂。下面我们从一些测试开始,来认识、了解一下这方面的特殊知识点:

 

自增列持久化问题

如果一个表拥有自增列,当前最大自增列值为9, 删除了自增列6、7、8、9的记录,重启MySQL服务后,再往表里面插入数据,自增列的值为6还是10呢?  如果表的存储引擎为MyISAM呢,又会是什么情况? 下面实验环境为MySQL 5.7.21

  1. mysql> drop table if exists test;

  1. Query OK, 0 rows affected (0.08 sec)

  1.  

  1. mysql> create table test(id int auto_increment primary key, name varchar(32)) ENGINE=InnoDB;

  1. Query OK, 0 rows affected (0.02 sec)

  1.  

  1.  

  1. mysql> insert into test(name)

  1.     -> select 'kkk1' from dual union all

  1.     -> select 'kkk2' from dual union all

  1.     -> select 'kkk3' from dual union all

  1.     -> select 'kkk4' from dual union all

  1.     -> select 'kkk5' from dual union all

  1.     -> select 'kkk6' from dual union all

  1.     -> select 'kkk7' from dual union all

  1.     -> select 'kkk8' from dual union all

  1.     -> select 'kkk9' from dual;

  1. Query OK, 9 rows affected (0.01 sec)

  1. Records: 9  Duplicates: 0  Warnings: 0

  1.  

  1.  

  1. mysql> select * from test;

  1. +----+------+

  1. | id | name |

  1. +----+------+

  1. |  1 | kkk1 |

  1. |  2 | kkk2 |

  1. |  3 | kkk3 |

  1. |  4 | kkk4 |

  1. |  5 | kkk5 |

  1. |  6 | kkk6 |

  1. |  7 | kkk7 |

  1. |  8 | kkk8 |

  1. |  9 | kkk9 |

  1. +----+------+

  1. 9 rows in set (0.00 sec)

  1.  

  1. mysql> delete from test where id>=6;

  1. Query OK, 4 rows affected (0.00 sec)

重启MySQL服务后,然后我们插入一条记录,字段ID会从什么值开始呢? 如下所示,如果表的存储引擎为InnoDB,那么插入的数据的自增字段值为6.

接下来,我们创建一个MyISAM类型的测试表。如下所示:

  1. mysql> drop table if exists test;

  1. Query OK, 0 rows affected (0.01 sec)

  1.  

  1. mysql> create table test(id int auto_increment  primary key, name varchar(32)) engine=MyISAM;

  1. Query OK, 0 rows affected (0.02 sec)

  1.  

  1. mysql>

  1.  

  1. insert into test(name)

  1. select 'kkk1' from dual union all

  1. select 'kkk2' from dual union all

  1. select 'kkk3' from dual union all

  1. select 'kkk4' from dual union all

  1. select 'kkk5' from dual union all

  1. select 'kkk6' from dual union all

  1. select 'kkk7' from dual union all

  1. select 'kkk8' from dual union all

  1. select 'kkk9' from dual;

  1.  

  1.  

  1. mysql> delete from test where id>=6;

  1. 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列的行为。

  1. mysql> drop table if exists test;

  1. Query OK, 0 rows affected (0.01 sec)

  1.  

  1. mysql> create table test(id int auto_increment primary key, name varchar(32));

  1. Query OK, 0 rows affected (0.02 sec)

  1.  

  1. mysql> select @@sql_mode;

  1. +-------------------------------------------------------------------------------------------------------------------------------------------+

  1. | @@sql_mode                                                                                                                                |

  1. +-------------------------------------------------------------------------------------------------------------------------------------------+

  1. | 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. +-------------------------------------------------------------------------------------------------------------------------------------------+

  1. 1 row in set (0.00 sec)

  1.  

  1. mysql> insert into test(id, name) value(0, 'kerry');

  1. Query OK, 1 row affected (0.00 sec)

  1.  

  1. mysql> select * from test;

  1. +----+-------+

  1. | id | name  |

  1. +----+-------+

  1. |  1 | kerry |

  1. +----+-------+

  1. 1 row in set (0.00 sec)

  1.  

  1. mysql>

如上所示,如果在SQL模式里面没有设置NO_AUTO_VALUE_ON_ZERO的话,那么在默认设置下,自增列默认一般从1开始自增,插入0或者null代表生成下一个自增长值。如果用户希望插入的值为0,而该列又是自增长的,那么这个选项就必须设置

  1. 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";

  1. Query OK, 0 rows affected (0.00 sec)

  1.  

  1. mysql> insert into test(id, name) value(0, 'kerry');

  1. Query OK, 1 row affected (0.01 sec)

  1.  

  1. mysql> select * from test;

  1. +----+-------+

  1. | id | name  |

  1. +----+-------+

  1. |  0 | kerry |

  1. |  1 | kerry |

  1. +----+-------+

  1. 2 rows in set (0.00 sec)

  1.  

  1. mysql>

2:如果把一个NULL值插入到一个AUTO_INCREMENT数据列里去,MySQL将自动生成下一个序列编号。如下所示,这个语法对于熟悉SQL Server中自增字段的人来来看,简直就是不可思议的事情。

  1. mysql> drop table if exists test;

  1. Query OK, 0 rows affected (0.03 sec)

  1.  

  1. mysql> create table test(id int auto_increment primary key, name varchar(32));

  1. Query OK, 0 rows affected (0.05 sec)

  1.  

  1. mysql> insert into test(id , name) value(null, 'kerry');

  1. Query OK, 1 row affected (0.00 sec)

  1.  

  1. mysql> select * from test;

  1. +----+-------+

  1. | id | name  |

  1. +----+-------+

  1. |  1 | kerry |

  1. +----+-------+

  1. 1 row in set (0.00 sec)

3:获取当前自增列的值

获取当前自增列的值,可以使用 LAST_INSERT_ID函数,注意,这个是一个系统函数,可获得自增列自动生成的最后一个值。但该函数只与服务器的本次会话过程中生成的值有关。如果在与服务器的本次会话中尚未生成AUTO_INCREMENT值,则该函数返回0

  1. mysql> select last_insert_id();

  1. +------------------+

  1. | last_insert_id() |

  1. +------------------+

  1. |                1 |

  1. +------------------+

  1. 1 row in set (0.00 sec)

  1.  

  1. mysql> insert into test(name) value('jimmy');

  1. Query OK, 1 row affected (0.00 sec)

  1.  

  1. mysql> select last_insert_id();

  1. +------------------+

  1. | last_insert_id() |

  1. +------------------+

  1. |                2 |

  1. +------------------+

  1. 1 row in set (0.00 sec)

  1.  

  1. mysql> select * from test;

  1. +----+-------+

  1. | id | name  |

  1. +----+-------+

  1. |  1 | kerry |

  1. |  2 | jimmy |

  1. +----+-------+

  1. 2 rows in set (0.00 sec)

如果要获取自增列的下一个值,那么可以使用show create table tablename查看。如下截图所示

4:自增列跳号

MySQL中,自增字段可以跳号:可以插入一条指定自增列值的记录(即使插入的值大于自增列的最大值),如下所示,当前自增列最大值为1,我插入一个200的值,然后就会以200为基础继续自增,而且我还可以继续插入ID=100的记录,无需任何额外设置。

  1. mysql> select * from test;

  1. +----+-------+

  1. | id | name  |

  1. +----+-------+

  1. |  1 | kerry |

  1. +----+-------+

  1. 1 row in set (0.00 sec)

  1.  

  1. mysql> insert into test value(200, 'test');

  1. Query OK, 1 row affected (0.01 sec)

  1.  

  1. mysql> select * from test;

  1. +-----+-------+

  1. | id  | name  |

  1. +-----+-------+

  1. |   1 | kerry |

  1. | 200 | test  |

  1. +-----+-------+

  1. 2 rows in set (0.00 sec)

  1.  

  1. mysql> insert into test(name) value('test2');

  1. Query OK, 1 row affected (0.01 sec)

  1.  

  1. mysql> select * from test;

  1. +-----+-------+

  1. | id  | name  |

  1. +-----+-------+

  1. |   1 | kerry |

  1. | 200 | test  |

  1. | 201 | test2 |

  1. +-----+-------+

  1. 3 rows in set (0.00 sec)

  1.  

  1. mysql>

  1. mysql> insert into test(id, name) value(100, 'ken');

  1. Query OK, 1 row affected (0.01 sec)

  1.  

  1. mysql> select * from test;

  1. +-----+-------+

  1. | id  | name  |

  1. +-----+-------+

  1. |   1 | kerry |

  1. | 100 | ken   |

  1. | 200 | test  |

  1. | 201 | test2 |

  1. +-----+-------+

  1. 4 rows in set (0.00 sec)

另外一个是关于自增列逻辑跳号问题,在一个事务里面,使用遇到事务回滚,自增列就会跳号,如下所示,id从201 跳到 203了。

  1. mysql> begin;

  1. Query OK, 0 rows affected (0.00 sec)

  1.  

  1. mysql> insert into test(name) value('kkk');

  1. Query OK, 1 row affected (0.00 sec)

  1.  

  1. mysql> select * from test;

  1. +-----+-------+

  1. | id  | name  |

  1. +-----+-------+

  1. |   1 | kerry |

  1. | 100 | ken   |

  1. | 200 | test  |

  1. | 201 | test2 |

  1. | 202 | kkk   |

  1. +-----+-------+

  1. 5 rows in set (0.00 sec)

  1.  

  1. mysql> rollback;

  1. Query OK, 0 rows affected (0.00 sec)

  1.  

  1. mysql> insert into test(name) value('kkk');

  1. Query OK, 1 row affected (0.00 sec)

  1.  

  1. mysql> select * from test;

  1. +-----+-------+

  1. | id  | name  |

  1. +-----+-------+

  1. |   1 | kerry |

  1. | 100 | ken   |

  1. | 200 | test  |

  1. | 201 | test2 |

  1. | 203 | kkk   |

  1. +-----+-------+

  1. 5 rows in set (0.00 sec)

当然,无论MySQL还是其他关系型数据库,都会遇到这种逻辑跳号的情况,例如ORACLE的序列也会存在这种逻辑跳号问题。为提高自增列的生成效率,都将生成自增值的操作设计为非事务性操作,表现为当事务回滚时,事务中生成的自增值不会被回滚。

5:truncate table操作会引起自增列从头开始计数

  1. mysql> truncate table test;

  1. Query OK, 0 rows affected (0.01 sec)

  1.  

  1. mysql> insert into test(name) value('kerry');

  1. Query OK, 1 row affected (0.00 sec)

  1.  

  1. mysql> select * from test;

  1. +----+-------+

  1. | id | name  |

  1. +----+-------+

  1. |  1 | kerry |

  1. +----+-------+

  1. 1 row in set (0.00 sec)

  1.  

  1. mysql>

6:修改AUTO_INCREMENT的值来修改自增起始值。

  1. mysql> select * from test;

  1. +----+-------+

  1. | id | name  |

  1. +----+-------+

  1. |  1 | kerry |

  1. +----+-------+

  1. 1 row in set (0.00 sec)

  1.  

  1. mysql> alter table test auto_increment=100;

  1. Query OK, 0 rows affected (0.00 sec)

  1. Records: 0  Duplicates: 0  Warnings: 0

  1.  

  1. mysql> insert into test(name) value('k3');

  1. Query OK, 1 row affected (0.00 sec)

  1.  

  1. mysql> select * from test;

  1. +-----+-------+

  1. | id  | name  |

  1. +-----+-------+

  1. |   1 | kerry |

  1. | 100 | k3    |

  1. +-----+-------+

  1. 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)相关知识点总结的更多相关文章

  1. 怎么重置mysql的自增列AUTO_INCREMENT初时值

    重置 MySQL 自增列 AUTO_INCREMENT 初时值 注意, 使用以下任意方法都会将现有数据删除. 方法一: delete from tb1; ALTER TABLE tbl AUTO_IN ...

  2. (转)mysql自增列导致主键重复问题分析

    mysql自增列导致主键重复问题分析...  原文:http://www.cnblogs.com/cchust/p/3914935.html 前几天开发童鞋反馈一个利用load data infile ...

  3. mysql自增列导致主键重复问题分析。。。

    前几天开发童鞋反馈一个利用load data infile命令导入数据主键冲突的问题,分析后确定这个问题可能是mysql的一个bug,这里提出来给大家分享下.以免以后有童鞋遇到类似问题百思不得其解,难 ...

  4. Mysql 自增列 主键

    Mysql中假如有 ID Int auto_increment, CID varchar(36). 通常情况下都是 ID设置为主键. 假如要设置CID为主键.自增列ID必需是唯一索引. create ...

  5. MySQL自增列锁模式 innodb_autoinc_lock_mode不同参数下性能测试

    对于innodb_autoinc_lock_mode 各种参数的值的含义,网上也有各种详解,看完觉得意犹未尽,这里不做阐述,只动手测试,看看性能上,到底有没有理论上所说的差别.对于自增列的锁定,据说是 ...

  6. 关于sparksql中设置自定义自增列的相关要点(工作共踩过的坑-1)

    小白终于进入了职场,从事大数据方面的工作! 分到项目组了,搬砖的时候遇到了一个这样的问题. 要求:用spark实现oracle的存储过程中计算部分. 坑:由于报表中包含了一个ID字段,其要求是不同的区 ...

  7. mysql自增字段AUTO_INCREMENT重排或归零

    由于删除了某些记录行,导致自增字段不连续了,重排或归零的方法: 方法1:truncate table 你的表名//这样不但重新定位自增的字段,而且会将表里的数据全部删除,慎用! 方法2:delete ...

  8. 重置Mysql自增列的开始序号

    ALTER TABLE  TableName AUTO_INCREMENT = 5; 代表重新从5开始(包括5)

  9. 关于MySQL自增主键的几点问题(上)

    前段时间遇到一个InnoDB表自增锁导致的问题,最近刚好有一个同行网友也问到自增锁的疑问,所以抽空系统的总结一下,这两个问题下篇会有阐述. 1. 划分三种插入类型 这里区分一下几种插入数据行的类型,便 ...

随机推荐

  1. [Swift]LeetCode682. 棒球比赛 | Baseball Game

    You're now a baseball game point recorder. Given a list of strings, each string can be one of the 4 ...

  2. 破解第一课:NOP绕过登录界面

    第一步 打开软件,任意输入密码,提示“用户密码错误还有2次机会” 第二步 OD载入软件,右键-----中文搜索引擎---智能搜索 按下CTRL+F,打开查找,输入“密码错误”,在结果中双击找到的结果 ...

  3. 学习springboot

    一般而言,写个Javaweb应用搭建环境都可能要几十分钟,下载个tomcat服务器,再加上各种xml配置等等,很烦躁,而且每个web应用的配置还差不多,都是什么web.xml,application. ...

  4. 【Python3爬虫】第一个Scrapy项目

    Python版本:3.5    IDE:Pycharm 今天跟着网上的教程做了第一个Scrapy项目,遇到了很多问题,花了很多时间终于解决了== 一.Scrapy终端(scrapy shell) Sc ...

  5. Unity资源打包学习笔记(一)、详解AssetBundle的流程

    转载请标明出处:http://www.cnblogs.com/zblade/ 本文参照unity官网上对于assetBundle的一系列讲解,主要针对assetbundle的知识点做一个梳理笔记,也为 ...

  6. C#2.0导航

    主要特性 泛型 类型和方法的参数化 可空类型 值类型可为null 委托 更简化的方式 迭代器 简单的foreach,不简单的状态机

  7. centos7下误执行chmod -R 777 /后的权限修复方法

    今天由于权限问题zz一般把/usr/bin和/usr/lib两个目录用chmod -R 777执行了一遍,结果各种问题出现,su root就报su:鉴定故障的错误.然后上网找教程很多都要求在root权 ...

  8. ReentrantLock原理学习

    上文我们学习了ReentrantLock的基本用法,在最后我们留下了一个问题,ReentrantLock获取的锁是什么锁呢?本文我们就从源码的角度来一探究竟.本文涉及到的源码对应JDK版本为1.8. ...

  9. css加载会造成阻塞吗

    本文由云+社区发表 作者:嘿嘿嘿 可能大家都知道,js执行会阻塞DOM树的解析和渲染,那么css加载会阻塞DOM树的解析和渲染吗?接下来,我就来对css加载对DOM树的解析和渲染的影响做一个测试. 为 ...

  10. -1-7 java 网络编程基本知识点 计算机网络 TCP/IP协议栈 通信必备 tcp udp

    计算机网络 是指将地理位置不同的具有独立功能的多台计算机及其外部设备,通过通信线路连接起来, 在网络操作系统,网络管理软件及网络通信协议的管理和协调下,实现资源共享和信息传递的计算机系统. 网络编程 ...