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

  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]LeetCode1009. 十进制整数的补码 | Complement of Base 10 Integer

    Every non-negative integer N has a binary representation.  For example, 5 can be represented as &quo ...

  2. Mongodb的集合插入一个字段

    在mongodb的现有集合中增加一个字段: db.rules.update({"_id" : ObjectId("587e5e0e09a4670334208e88&quo ...

  3. 【转】CGI

    CGI是什么 (一): CGI是Common Gateway Interface 的简称.是一个用于定Web服务器与外部程序之间通信方式的标准,使得外部程序能生成HTML.图像或者其他内容,而服务器处 ...

  4. 单机部署redis5.0集群环境

    #安装rediscd redis-5.0.0makemake install #部署集群mkdir redis_clustermkdir -p redis_cluster/{7000,7001,700 ...

  5. ASP.NET Core WebAPI中的分析工具MiniProfiler

    介绍 作为一个开发人员,你知道如何分析自己开发的Api性能么? 在Visual Studio和Azure中, 我们可以使用Application Insight来监控项目.除此之外我们还可以使用一个免 ...

  6. Android创建或删除了文件,在电脑端查看的时候,却没有对应的变化,处理办法

    在Android应用中,碰到一个问题,在代码中执行创建或者删除某个文件后,在系统的文件管理器中能够相应地看到文件的变化,但是插在电脑上查看的时候,却看不到改文件的变化.同时,当创建文件后,在系统中的某 ...

  7. 前后端数据加密传输 RSA非对称加密

    任务需求:要求登陆时将密码加密之后再进行传输到后端. 经过半天查询摸索折腾,于是有了如下成果: 加密方式:RSA非对称加密.实现方式:公钥加密,私钥解密.研究进度:javascript与java端皆已 ...

  8. nodejs搭建web项目

    如果要使用cnpm可安装淘宝cnpm镜像(事实证明不建议使用,因为cnpm和npm有一些包不同步)npm install -g cnpm --registry=https://registry.npm ...

  9. CSRF 漏洞测试

    CSRF简介: CSRF中文名:跨站请求伪造,英文译为:Cross-site request forgery,CSRF攻击就是attacker(攻击者)利用victim(受害者)尚未失效的身份认证信息 ...

  10. scrapy爬虫学习系列一:scrapy爬虫环境的准备

    系列文章列表: scrapy爬虫学习系列一:scrapy爬虫环境的准备:      http://www.cnblogs.com/zhaojiedi1992/p/zhaojiedi_python_00 ...