一、概述
在數據庫應用中,我們經常需要用到自動遞增的唯一編號來標識記錄。在MySQL中,可通過數據列的auto_increment屬性來自動生成。可在建表時可用“auto_increment=n”選項來指定一個自增的初始值。可用“alter table table_name auto_increment=n”命令來重設自增的起始值,當然在設置的時候Mysql會取數據表中auto_increment列的最大值 + 1與n中的較大者作為新的auto_increment值。
Myql的auto_increment屬性具有以下特性:
-
- 具有auto_increment屬性的數據列應該是一個正數序列,如果把該數據列聲明為UNSIGNED,這樣序列的編號個數可增加一倍。比如tinyint數據列的最大編號是127,如果加上UNSIGNED,那么最大編號變為255
- auto_increment數據列必須有唯一索引,以避免序號重復;必須具備NOT NULL屬性
實際應用中發現,在delete掉某張innoDB表的全部數據並重啟Mysql會導致該表的auto_increment列變為1。特測試多種情況下auto_increment列的變化並記錄如下。
二、實驗
1、innoDB與MyISAM對比
(1)首先,創建一張引擎為innoDB的表測試一下delete掉所有數據然后重啟Mysql之后,auto_increment的情況:
mysql> CREATE TABLE `table1` ( -> `id` bigint(20) NOT NULL auto_increment, -> `create_time` datetime DEFAULT NULL, -> PRIMARY KEY (`id`) -> ) ENGINE=InnoDB DEFAULT CHARSET=utf8; Query OK, 0 rows affected mysql> insert into table1(create_time) values (now()); Query OK, 1 row affected mysql> insert into table1(create_time) values (now()); Query OK, 1 row affected mysql> insert into table1(create_time) values (now()); Query OK, 1 row affected mysql> insert into table1(create_time) values (now()); Query OK, 1 row affected mysql> insert into table1(create_time) values (now()); Query OK, 1 row affected mysql> select * from table1; +----+---------------------+ | id | create_time | +----+---------------------+ | 1 | 2017-02-28 16:25:11 | | 2 | 2017-02-28 16:25:21 | | 3 | 2017-02-28 16:25:23 | | 4 | 2017-02-28 16:25:23 | | 5 | 2017-02-28 16:25:24 | | 6 | 2017-02-28 16:25:26 | +----+---------------------+ 6 rows in set mysql> delete from table1; Query OK, 6 rows affected mysql> select * from table1; Empty set mysql> select auto_increment from information_schema.tables where table_schema = database() and table_name='table1'; +----------------+ | auto_increment | +----------------+ | 7 | +----------------+ 1 row in set
可見,執行delete操作清空表之后,表table1的auto_increment值仍然是正常的。重啟數據庫之后:
mysql> select auto_increment from information_schema.tables where table_schema = database() and table_name='table1'; +----------------+ | auto_increment | +----------------+ | 1 | +----------------+ 1 row in set
可見,table1表的auto_increment值變成了1。
結論:innoDB引擎的表,在執行delete清空操作之后,表的auto_increment值不會受到影響;一旦重啟Mysql數據庫,那么auto_increment值將變成1!
(2)下面我們創建一個引擎為MyISAM的表,測試delete掉所有數據,並重啟數據庫之后auto_increment的值如何變化:
mysql> CREATE TABLE `table2` ( -> `id` bigint(20) NOT NULL auto_increment, -> `create_time` datetime DEFAULT NULL, -> PRIMARY KEY (`id`) -> ) ENGINE=MyISAM DEFAULT CHARSET=utf8; Query OK, 0 rows affected mysql> insert into table2(create_time) values (now()); Query OK, 1 row affected mysql> mysql> mysql> insert into table2(create_time) values (now()); Query OK, 1 row affected mysql> insert into table2(create_time) values (now()); Query OK, 1 row affected mysql> insert into table2(create_time) values (now()); Query OK, 1 row affected mysql> insert into table2(create_time) values (now()); Query OK, 1 row affected mysql> insert into table2(create_time) values (now()); Query OK, 1 row affected mysql> select * from table2; +----+---------------------+ | id | create_time | +----+---------------------+ | 1 | 2017-02-28 17:05:22 | | 2 | 2017-02-28 17:05:25 | | 3 | 2017-02-28 17:05:26 | | 4 | 2017-02-28 17:05:27 | | 5 | 2017-02-28 17:05:28 | | 6 | 2017-02-28 17:05:29 | +----+---------------------+ 6 rows in set mysql> delete from table2; Query OK, 6 rows affected mysql> select * from table2; Empty set mysql> select auto_increment from information_schema.tables where table_schema = database() and table_name='table2'; +----------------+ | auto_increment | +----------------+ | 7 | +----------------+ 1 row in set
delete清空操作並不會對table2的auto_increment產生任何影響。重啟數據庫之后:
mysql> select auto_increment from information_schema.tables where table_schema = database() and table_name='table2'; +----------------+ | auto_increment | +----------------+ | 7 | +----------------+ 1 row in set
可見,表table2的auto_increment仍然為7。
結論:MyISAM引擎的表,在執行delete操作之后,表的auto_increment值不會受到影響;重啟Mysql數據庫,auto_increment值也不會受到影響!
2、創建表時指定auto_increment
本節我們測試創建innoDB引擎的表時指定auto_increment會不會對auto_increment產生影響:
mysql> CREATE TABLE `table3` ( -> `id` bigint(20) NOT NULL auto_increment, -> `create_time` datetime DEFAULT NULL, -> PRIMARY KEY (`id`) -> ) ENGINE=InnoDB auto_increment=1000 DEFAULT CHARSET=utf8; Query OK, 0 rows affected mysql> select auto_increment from information_schema.tables where table_schema = database() and table_name='table3'; +----------------+ | auto_increment | +----------------+ | 1000 | +----------------+ 1 row in set mysql> insert into table3(create_time) values (now()); Query OK, 1 row affected mysql> insert into table3(create_time) values (now()); Query OK, 1 row affected mysql> insert into table3(create_time) values (now()); Query OK, 1 row affected mysql> insert into table3(create_time) values (now()); Query OK, 1 row affected mysql> insert into table3(create_time) values (now()); Query OK, 1 row affected mysql> select * from table3; +------+---------------------+ | id | create_time | +------+---------------------+ | 1000 | 2017-02-28 17:15:13 | | 1001 | 2017-02-28 17:15:14 | | 1002 | 2017-02-28 17:15:15 | | 1003 | 2017-02-28 17:15:15 | | 1004 | 2017-02-28 17:15:16 | +------+---------------------+ 5 rows in set mysql> delete from table3; Query OK, 5 rows affected mysql> select * from table3; Empty set mysql> select auto_increment from information_schema.tables where table_schema = database() and table_name='table3'; +----------------+ | auto_increment | +----------------+ | 1005 | +----------------+ 1 row in set
可見,delete操作並不會影響到表table3的auto_increment值。重啟數據庫之后:
mysql> select auto_increment from information_schema.tables where table_schema = database() and table_name='table3'; +----------------+ | auto_increment | +----------------+ | 1 | +----------------+ 1 row in set
表table3的auto_increment變成了1。
結論:在創建innoDB表時,無論指定或不指定auto_increment,delete清空+重啟數據庫都會使表的auto_increment值變成1。
3、delete的時候添加where 1
本節討論在執行delete操作時,加where 1:
mysql> CREATE TABLE `table4` ( -> `id` bigint(20) NOT NULL auto_increment, -> `create_time` datetime DEFAULT NULL, -> PRIMARY KEY (`id`) -> ) ENGINE=InnoDB DEFAULT CHARSET=utf8; Query OK, 0 rows affected mysql> insert into table4(create_time) values (now()); Query OK, 1 row affected mysql> mysql> insert into table4(create_time) values (now()); Query OK, 1 row affected mysql> insert into table4(create_time) values (now()); Query OK, 1 row affected mysql> insert into table4(create_time) values (now()); Query OK, 1 row affected mysql> insert into table4(create_time) values (now()); Query OK, 1 row affected mysql> insert into table4(create_time) values (now()); Query OK, 1 row affected mysql> insert into table4(create_time) values (now()); Query OK, 1 row affected mysql> select * from table4; +----+---------------------+ | id | create_time | +----+---------------------+ | 1 | 2017-02-28 17:21:33 | | 2 | 2017-02-28 17:21:34 | | 3 | 2017-02-28 17:21:35 | | 4 | 2017-02-28 17:21:36 | | 5 | 2017-02-28 17:21:36 | | 6 | 2017-02-28 17:21:37 | | 7 | 2017-02-28 17:21:38 | +----+---------------------+ 7 rows in set mysql> delete from table4 where 1; Query OK, 7 rows affected mysql> select auto_increment from information_schema.tables where table_schema = database() and table_name='table4'; +----------------+ | auto_increment | +----------------+ | 8 | +----------------+ 1 row in set
重啟數據庫之后:
mysql> select auto_increment from information_schema.tables where table_schema = database() and table_name='table4'; +----------------+ | auto_increment | +----------------+ | 1 | +----------------+ 1 row in set
可見,網上的所流傳的delete清空操作時添加where 1並沒用。
結論:delete innoDB表時,添加或不添加where 1,在數據庫重啟之后auto_increment都會被重置為1。
4、如果表中有數據,但是數據id小於auto_increment會怎么樣?
本節測試當innoDB表中有數據,但是auto_increment列最大的那個值小於表的auto_increment值會怎樣。我們先插入一些數據到表中,然后刪除末尾的幾條數據:
mysql> CREATE TABLE `table5` ( -> `id` bigint(20) NOT NULL auto_increment, -> `create_time` datetime DEFAULT NULL, -> PRIMARY KEY (`id`) -> ) ENGINE=InnoDB DEFAULT CHARSET=utf8; Query OK, 0 rows affected mysql> insert into table5(create_time) values (now()); Query OK, 1 row affected mysql> insert into table5(create_time) values (now()); Query OK, 1 row affected mysql> insert into table5(create_time) values (now()); Query OK, 1 row affected mysql> insert into table5(create_time) values (now()); Query OK, 1 row affected mysql> insert into table5(create_time) values (now()); Query OK, 1 row affected mysql> insert into table5(create_time) values (now()); Query OK, 1 row affected mysql> insert into table5(create_time) values (now()); Query OK, 1 row affected mysql> insert into table5(create_time) values (now()); Query OK, 1 row affected mysql> select * from table5; +----+---------------------+ | id | create_time | +----+---------------------+ | 1 | 2017-02-28 17:29:29 | | 2 | 2017-02-28 17:29:30 | | 3 | 2017-02-28 17:29:30 | | 4 | 2017-02-28 17:29:30 | | 5 | 2017-02-28 17:29:31 | | 6 | 2017-02-28 17:29:31 | | 7 | 2017-02-28 17:29:32 | | 8 | 2017-02-28 17:29:32 | +----+---------------------+ 8 rows in set mysql> delete from table5 where id > 4; Query OK, 4 rows affected mysql> select * from table5; +----+---------------------+ | id | create_time | +----+---------------------+ | 1 | 2017-02-28 17:29:29 | | 2 | 2017-02-28 17:29:30 | | 3 | 2017-02-28 17:29:30 | | 4 | 2017-02-28 17:29:30 | +----+---------------------+ 4 rows in set mysql> select auto_increment from information_schema.tables where table_schema = database() and table_name='table5'; +----------------+ | auto_increment | +----------------+ | 9 | +----------------+ 1 row in set
重啟數據庫之后:
mysql> select auto_increment from information_schema.tables where table_schema = database() and table_name='table5'; +----------------+ | auto_increment | +----------------+ | 5 | +----------------+ 1 row in set
哇塞,奇跡發生了,table5的auto_increment居然變成了5。由此我們可以得出以下結論。
結論:Mysql數據庫在重啟之后,innoDB表的auto_increment值將會被設置為表中auto_increment列的最大值 + 1。
三、深究
為什么會出現上述情況呢?
這是因為,Mysql數據庫的的auto_increment值是保存在內存中的,innoDB引擎的表的auto_increment在數據庫服務停止時並不會做持久化操作,Mysql會在下次數據庫重啟的時候,相當於通過執行語句:
select max(id) maxId from table; alter table auto_increment = maxId + 1;
來設置表table的auto_increment值。
嚴格意義上來說這是Mysql的一個bug。這個bug將會在8.0版本中得到修復。關於8.0版本的內容的情況,詳見:MySQL 8.0.0 版本發布,亮點都在這了! 以及 MySQL 8.0發布,是時候與MyISAM說再見了
因為目前8.0版本穩定版尚未發布,所以目前為了避免被這個bug坑到,只能將引擎換為MyISAM或者從程序上去控制。