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還有一些相關知識點,這里沒有做總結,主要是沒有遇到過相關場景。

 

 

https://www.cnblogs.com/kerrycode/p/9294767.html 

參考資料:

 

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

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM