參考:《mysql內核:innodb存儲引擎》 第九章
mysql實戰45講—自增主鍵爲什麼不是連續的?
[MySQL源碼] Innodb如何處理auto_inc值:https://yq.aliyun.com/articles/40926
[MySQL Bug]bug#61209簡析:https://yq.aliyun.com/articles/40929
測試環境:Mysql 5.6.43版本
--2、上述5個問題的測試
--數據庫參數設置
mysql> show variables like '%auto_increment%';
+--------------------------+-------+
| Variable_name | Value |
+--------------------------+-------+
| auto_increment_increment | 10 | //自增值遞增的間隔,測試設置值爲 10
| auto_increment_offset | 5 | //自增值的起始偏移,測試設置值爲 5
+--------------------------+-------+
2 rows in set (0.00 sec)
mysql> show variables like '%innodb_autoinc_lock_mode%';
+--------------------------+-------+
| Variable_name | Value |
+--------------------------+-------+
| innodb_autoinc_lock_mode | 1 | //申請後立即釋放,但批量操作 比如 insert...select 、load data 之類的操作 還是回退到0的方式 即提交結束後 釋放自增鎖
+--------------------------+-------+
1 row in set (0.00 sec)
--創建測試表並測試數據,id爲自增值 ,以5位偏移量 、每次自增10
mysql> create table test_table_autoincr
(id int auto_increment,
name varchar(10),
id_card int,
primary key(id),
unique key idx_test_table_autoincr_id_card(id_card)
)engine=innodb;
Query OK, 0 rows affected (0.22 sec)
--插入測試數據
insert into test_table_autoincr values (null,'aa',11);
commit;
--如上問題1的現象:數據庫重啓後,會取得該表的最大自增值(SELECT MAX(col_name) FROM TABLE) +1 賦給下一條記錄,可能會造成自增值的回退
--插入待刪除的name=aa_del的一條數據,然後刪除
insert into test_table_autoincr values (null,'aa_del',1111);
commit;
mysql> delete from test_table_autoincr where id=15;
mysql> commit;
mysql> select * from test_table_autoincr;
+----+------+---------+
| id | name | id_card |
+----+------+---------+
| 5 | aa | 11 |
+----+------+---------+
1 row in set (0.00 sec)
--查看下個自增值爲AUTO_INCREMENT=25,因上個自增值爲15的值被刪除了
mysql> show create table test_table_autoincr\G
*************************** 1. row ***************************
Table: test_table_autoincr
Create Table: CREATE TABLE `test_table_autoincr` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(10) COLLATE utf8_bin DEFAULT NULL,
`id_card` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `idx_test_table_autoincr_id_card` (`id_card`)
) ENGINE=InnoDB AUTO_INCREMENT=25 DEFAULT CHARSET=utf8 COLLATE=utf8_bin
1 row in set (0.00 sec)
--重啓數據庫
[root@localmysql-source ~]# service mysql restart
Shutting down MySQL....[ OK ]
Starting MySQL.[ OK ]
--自增值回退爲AUTO_INCREMENT=6 也就是該表的最大自增值(SELECT MAX(col_name) FROM TABLE) +1 (即是目前自增值的間隔是10的情況下 也是+1,下次插入自增值時 會重新計算 )
mysql> show create table test_table_autoincr\G
*************************** 1. row ***************************
Table: test_table_autoincr
Create Table: CREATE TABLE `test_table_autoincr` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(10) COLLATE utf8_bin DEFAULT NULL,
`id_card` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `idx_test_table_autoincr_id_card` (`id_card`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8 COLLATE=utf8_bin
1 row in set (0.00 sec)
--繼續插入數據
insert into test_table_autoincr values (null,'bb',22),(null,'cc',33),(null,'dd',44);
commit;
--查看當前表數據,發現自增值連續,不受到之前delete數據的影響
mysql> select * from test_table_autoincr;
+----+------+---------+
| id | name | id_card |
+----+------+---------+
| 5 | aa | 11 |
| 15 | bb | 22 |
| 25 | cc | 33 |
| 35 | dd | 44 |
+----+------+---------+
4 rows in set (0.00 sec)
--查看錶狀態,可看下個自增值爲AUTO_INCREMENT=45
mysql> show create table test_table_autoincr\G
*************************** 1. row ***************************
Table: test_table_autoincr
Create Table: CREATE TABLE `test_table_autoincr` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(10) COLLATE utf8_bin DEFAULT NULL,
`id_card` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `idx_test_table_autoincr_id_card` (`id_card`)
) ENGINE=InnoDB AUTO_INCREMENT=45 DEFAULT CHARSET=utf8 COLLATE=utf8_bin
1 row in set (0.00 sec)
--如上問題2的現象:id_card唯一索引衝突後,下一條自增值不連續了。 45的自增值因唯一索引衝突而丟失
mysql> insert into test_table_autoincr values (null,'ee',44);
ERROR 1062 (23000): Duplicate entry '44' for key 'idx_test_table_autoincr_id_card'
mysql> insert into test_table_autoincr values (null,'ee',55);
mysql> select * from test_table_autoincr;
+----+------+---------+
| id | name | id_card |
+----+------+---------+
| 5 | aa | 11 |
| 15 | bb | 22 |
| 25 | cc | 33 |
| 35 | dd | 44 |
| 55 | ee | 55 |
+----+------+---------+
5 rows in set (0.00 sec)
--如上問題3的現象:事務回滾後,下一條自增值不連續了。65的自增值因爲回滾而丟失
mysql> begin;
mysql> insert into test_table_autoincr values (null,'ff',66);
mysql> rollback;
mysql> insert into test_table_autoincr values (null,'ff',66);
mysql> commit;
mysql> select * from test_table_autoincr;
+----+------+---------+
| id | name | id_card |
+----+------+---------+
| 5 | aa | 11 |
| 15 | bb | 22 |
| 25 | cc | 33 |
| 35 | dd | 44 |
| 55 | ee | 55 |
| 75 | ff | 66 |
+----+------+---------+
6 rows in set (0.00 sec)
--如上問題4的現象:插入給定的自增值505,會造成和上一條的75自增值不連續了。
mysql> insert into test_table_autoincr values (505,'gg',77);
mysql> insert into test_table_autoincr values (null,'hh',88);
mysql> commit;
mysql> select * from test_table_autoincr;
+-----+------+---------+
| id | name | id_card |
+-----+------+---------+
| 5 | aa | 11 |
| 15 | bb | 22 |
| 25 | cc | 33 |
| 35 | dd | 44 |
| 55 | ee | 55 |
| 75 | ff | 66 |
| 505 | gg | 77 |
| 515 | hh | 88 |
+-----+------+---------+
8 rows in set (0.00 sec)
--其中如果不是按照自增值間隔和偏移量的規律來的話, 會造成和上一條以及下一條的自增值同時不連續了。 插入1000後 不符合自增長間隔和偏移量,下一條自增值要符合規律變爲1005 造成不連續
mysql> insert into test_table_autoincr values (1000,'ii',99);
mysql> insert into test_table_autoincr values (null,'jj',110);
mysql> insert into test_table_autoincr values (null,'kk',220);
mysql> commit;
mysql> select * from test_table_autoincr;
+------+------+---------+
| id | name | id_card |
+------+------+---------+
| 5 | aa | 11 |
| 15 | bb | 22 |
| 25 | cc | 33 |
| 35 | dd | 44 |
| 55 | ee | 55 |
| 75 | ff | 66 |
| 505 | gg | 77 |
| 515 | hh | 88 |
| 1000 | ii | 99 |
| 1005 | jj | 110 |
| 1015 | kk | 220 |
+------+------+---------+
11 rows in set (0.00 sec)
--如上問題5的現象:批量插入時,會分配的自增值個數爲上一次的2倍, 這樣分配下來 會造成一些自增值浪費 造成下一條自增值不連續了。
--測試表test_table_autoincr_2 插入4條數據,分四次申請的,第一次申請1個,第二次申請2個,第三次申請4個,這樣就是申請了7個 但實際只有4條數據,
create table test_table_autoincr_2 like test_table_autoincr;
insert into test_table_autoincr_2 values (null,'qwer',1111);
insert into test_table_autoincr_2 values (null,'adsf',2222);
insert into test_table_autoincr_2 values (null,'zxcv',3333);
insert into test_table_autoincr_2 values (null,'uiop',4444);
commit;
mysql> select * from test_table_autoincr_2;
+----+------+---------+
| id | name | id_card |
+----+------+---------+
| 5 | qwer | 1111 |
| 15 | adsf | 2222 |
| 25 | zxcv | 3333 |
| 35 | uiop | 4444 |
+----+------+---------+
4 rows in set (0.00 sec)
mysql> show create table test_table_autoincr_2\G
*************************** 1. row ***************************
Table: test_table_autoincr_2
Create Table: CREATE TABLE `test_table_autoincr_2` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(10) COLLATE utf8_bin DEFAULT NULL,
`id_card` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `idx_test_table_autoincr_id_card` (`id_card`)
) ENGINE=InnoDB AUTO_INCREMENT=45 DEFAULT CHARSET=utf8 COLLATE=utf8_bin
1 row in set (0.00 sec)
--測試表test_table_autoincr_3複製test_table_autoincr_2的數據後,再插入一條數據後,45、55、65自增值丟失 不連續了, 從第8個開始
create table test_table_autoincr_3 like test_table_autoincr_2;
insert into test_table_autoincr_3(name,id_card) select name,id_card from test_table_autoincr_2;
mysql> select * from test_table_autoincr_3;
+----+------+---------+
| id | name | id_card |
+----+------+---------+
| 5 | qwer | 1111 |
| 15 | adsf | 2222 |
| 25 | zxcv | 3333 |
| 35 | uiop | 4444 |
+----+------+---------+
4 rows in set (0.00 sec)
mysql> show create table test_table_autoincr_3\G
*************************** 1. row ***************************
Table: test_table_autoincr_3
Create Table: CREATE TABLE `test_table_autoincr_3` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(10) COLLATE utf8_bin DEFAULT NULL,
`id_card` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `idx_test_table_autoincr_id_card` (`id_card`)
) ENGINE=InnoDB AUTO_INCREMENT=75 DEFAULT CHARSET=utf8 COLLATE=utf8_bin
1 row in set (0.00 sec)
mysql> insert into test_table_autoincr_3 values (null,'hjlk',5555);
Query OK, 1 row affected (0.00 sec)
mysql> select * from test_table_autoincr_3;
+----+------+---------+
| id | name | id_card |
+----+------+---------+
| 5 | qwer | 1111 |
| 15 | adsf | 2222 |
| 25 | zxcv | 3333 |
| 35 | uiop | 4444 |
| 75 | hjlk | 5555 |
+----+------+---------+
5 rows in set (0.00 sec)