MySQL AUTO_INCREMENT 學習總結

来源:https://www.cnblogs.com/dbabd/archive/2018/12/25/10169197.html
-Advertisement-
Play Games

正文 之前有碰到過開發同事指出一張InnoDB表的自增列 AUTO_INCREMENT 值莫明的變大,由於這張表是通過mysqldump導出導入的。 問題排查: 1、首先,查看表表義的sql部分的 auto_increment 值部分是正常,所以排除是導入表問題所引起的; 2、最後,經過溝通瞭解懷疑 ...


正文

之前有碰到過開發同事指出一張InnoDB表的自增列 AUTO_INCREMENT 值莫明的變大,由於這張表是通過mysqldump導出導入的。

問題排查:

1、首先,查看表表義的sql部分的 auto_increment 值部分是正常,所以排除是導入表問題所引起的;
2、最後,經過溝通瞭解懷疑是插入時指定自增列的值,並且值過大,隨之發現自增列的值出錯時又進行大量刪除時引起的問題。

為了驗證這個懷疑的準確性,同時學習下InnoDB處理 AUTO_INCREMENT 的機制,因此在測試環境做了測試總結。

本文使用的MySQL版本為官方社區版 5.7.24

(root@localhost) [test] > select version();
+------------+
| version()  |
+------------+
| 5.7.24-log |
+------------+
1 row in set (0.00 sec)

測試環境測試表參考官方文檔:https://dev.mysql.com/doc/refman/5.7/en/innodb-auto-increment-handling.html

(root@localhost) [test] > show create table t1\G;
*************************** 1. row ***************************
       Table: t1
Create Table: CREATE TABLE `t1` (
  `c1` int(11) NOT NULL AUTO_INCREMENT,
  `c2` varchar(10) DEFAULT NULL,
  PRIMARY KEY (`c1`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
1 row in set (0.00 sec)

(root@localhost) [test] > show columns from t1;
+-------+-------------+------+-----+---------+----------------+
| Field | Type        | Null | Key | Default | Extra          |
+-------+-------------+------+-----+---------+----------------+
| c1    | int(11)     | NO   | PRI | NULL    | auto_increment |
| c2    | varchar(10) | YES  |     | NULL    |                |
+-------+-------------+------+-----+---------+----------------+
2 rows in set (0.00 sec)

AUTO_INCREMENT鎖模式

InnoDB自增鎖的模式由參數 innodb_autoinc_lock_mode 在啟動時指定,這是一個只讀參數,並不能在實例運行中動態修改參數值。參數值選項如下:

innodb_autoinc_lock_mode = {0|1|2}

InnoDB表insert語句主要可以分為三種類型:

  • Simple inserts(簡單插入)
    可以通過語句預先判斷插入的行數。包括不包含子查詢的單行、多行 INSERTREPLACE 語句,還有語句 INSERT ... ON DUPLICATE KEY UPDATE

  • Bulk inserts(大量插入)
    無法通過語句預先判斷插入的行數。包括 INSERT ... SELECTREPLACE ... SELECTLOAD DATA 語句。InnoDB每處理一行才會為 AUTO_INCREMENT 列分配一個值。

  • Mixed-mode inserts(混合模式插入)
    在簡單插入語句當中,有的行有為自增列指定值,而有的行沒有為自增列指定值。例如:

INSERT INTO t1 (c1,c2) VALUES (1,'a'), (NULL,'b'), (5,'c'), (NULL,'d'); 

其中c1為自增列。

還有一種混合模式插入語句 INSERT ... ON DUPLICATE KEY UPDATE ,這類語句InnoDB為自增列預分配的值有可能被使用,也有可能不被使用。

傳統鎖模式 (traditional)

innodb_autoinc_lock_mode = 0

在這個模式下,所有的 INSERT 語句在插入有自增屬性的列時都要獲取一個特殊的 AUTO-INC 表級鎖。該鎖的持有時間到語句結束(而不是到事務結束,一個事務中可能包含多條語句),它能夠確保為有自增屬性列在 INSERT 一行或者多行數據時分配連續遞增的值。

連續鎖模式 (consecutive)

innodb_autoinc_lock_mode = 1

這是預設的鎖模式。在這個模式下,大量插入每條語句執行時都將獲得特殊的表級 AUTO-INC 鎖,語句執行完成後釋放。每次只有一條語句可以執行並持有 AUTO-INC 鎖。

  • Bulk inserts(大量插入)
    • 如果大量插入的源表和目標表是不同的,則在源表第一行數據獲得共用鎖之後,目標表就加上 AUTO-INC 表鎖;
    • 如果大量插入的源表和目標表是同一張表,當源表選取所有行獲得共用鎖之後,目標表才加上 AUTO-INC 表鎖。
  • Simple inserts(簡單插入)
    • 通過 MUTEX(輕量級的鎖) 而不是 AUTO-INC特殊表鎖控制插入分配自增屬性列的值;
    • MUTEX 只在分配值的過程中持有,而無需等到語句結束,並且性能花銷更少;
    • 簡單插入不持有 AUTO-INC 鎖,但如果其他事務持有,需等待其他事務釋放,就像大量插入那樣。

交叉鎖模式 (interleaved)

innodb_autoinc_lock_mode = 2

在這種鎖模式下,沒有插入語句使用 AUTO-INC 表級鎖,並且多條語句可以併發執行。這是最快並且擴展性最好的鎖模式,但是如果binlog使用基於語句級複製的在從庫重放SQL語句時是不安全的。

AUTO_INCREMENT鎖模式使用說明

用於複製

  • 基於語句級別的複製,當 innodb_autoinc_lock_mode = 0 | 1 時,主從使用的是相同的自增列值。當 innodb_autoinc_lock_mode = 2 或者主從使用不同的 innodb_autoinc_lock_mode 時,主從無法保證使用相同的自增列值;
  • 基於行級別和複合模式的複製,innodb_autoinc_lock_mode 的所有取值都是安全的,因為SQL語句執行順序對基於行級別的複製沒影響。

自增值缺失與間隙

無論 AUTO_INCREMENT 處於哪種鎖模式下,即 innodb_autoinc_lock_mode 的所有取值情況下,在一個事務當中自增列分配的值是不能被回滾的。這會導致事務回滾了,但是自增列分配的值卻消失了,自增列分配的值是無法隨著事務回滾而重覆利用,這樣就自增列上的值就產生了間隙。

測試:

--1、 開啟一個事務
(root@localhost) [test] > begin;
Query OK, 0 rows affected (0.00 sec)

(root@localhost) [test] > insert into t1(c2) values('aa');
Query OK, 1 row affected (0.00 sec)

(root@localhost) [test] > select * from t1;
+----+------+
| c1 | c2   |
+----+------+
|  1 | aa   |
+----+------+
1 row in set (0.00 sec)

(root@localhost) [test] > show create table t1\G;
*************************** 1. row ***************************
       Table: t1
Create Table: CREATE TABLE `t1` (
  `c1` int(11) NOT NULL AUTO_INCREMENT,
  `c2` varchar(10) DEFAULT NULL,
  PRIMARY KEY (`c1`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8
1 row in set (0.00 sec)

--2、事務回滾,不提交
(root@localhost) [test] > rollback;
Query OK, 0 rows affected (0.00 sec)

(root@localhost) [test] > select * from t1;
Empty set (0.00 sec)

(root@localhost) [test] > show create table t1\G;
*************************** 1. row ***************************
       Table: t1
Create Table: CREATE TABLE `t1` (
  `c1` int(11) NOT NULL AUTO_INCREMENT,
  `c2` varchar(10) DEFAULT NULL,
  PRIMARY KEY (`c1`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8
1 row in set (0.00 sec) 

--3、再次開啟事務,插入值並提交
(root@localhost) [test] > begin;
Query OK, 0 rows affected (0.00 sec)

(root@localhost) [test] > insert into t1(c2) values('aaa');
Query OK, 1 row affected (0.02 sec)

(root@localhost) [test] > select * from t1;
+----+------+
| c1 | c2   |
+----+------+
|  2 | aaa  |
+----+------+
1 row in set (0.00 sec)

(root@localhost) [test] > commit;
Query OK, 0 rows affected (0.00 sec)

(root@localhost) [test] > show create table t1\G;
*************************** 1. row ***************************
       Table: t1
Create Table: CREATE TABLE `t1` (
  `c1` int(11) NOT NULL AUTO_INCREMENT,
  `c2` varchar(10) DEFAULT NULL,
  PRIMARY KEY (`c1`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8
1 row in set (0.00 sec)

可以看出自增列分配的值是不會再出現 c1=1 的。

為自增列指定NULL或者0

無論 AUTO_INCREMENT 處於哪種鎖模式下,如果在 INSERT 語句為自增列指定 NULL 或者 0 時,InnoDB認為並沒有為自增列指定值,同時也會為其分配值。

測試:

(root@localhost) [test] > select * from t1;
+----+------+
| c1 | c2   |
+----+------+
|  2 | aaa  |
+----+------+
1 row in set (0.00 sec)

(root@localhost) [test] > insert into t1 values(NULL,'bbb'),(0,'ccc');
Query OK, 2 rows affected (0.02 sec)
Records: 2  Duplicates: 0  Warnings: 0

(root@localhost) [test] > select * from t1;
+----+------+
| c1 | c2   |
+----+------+
|  2 | aaa  |
|  3 | bbb  |
|  4 | ccc  |
+----+------+
3 rows in set (0.00 sec)

(root@localhost) [test] > show create table t1\G;
*************************** 1. row ***************************
       Table: t1
Create Table: CREATE TABLE `t1` (
  `c1` int(11) NOT NULL AUTO_INCREMENT,
  `c2` varchar(10) DEFAULT NULL,
  PRIMARY KEY (`c1`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8
1 row in set (0.00 sec)

為自增列指定負值

無論 AUTO_INCREMENT 處於哪種鎖模式下,自增列分配值機制不會生效,即為自增列指定負值是不影響 AUTO_INCREMENT 值的。

測試:

(root@localhost) [test] > SELECT auto_increment FROM information_schema.tables where table_schema='test' and table_name='t1';
+----------------+
| auto_increment |
+----------------+
|              5 |
+----------------+
1 row in set (0.00 sec)

(root@localhost) [test] > insert into t1 values(-1,'ddd');
Query OK, 1 row affected (0.01 sec)

(root@localhost) [test] > select * from t1;
+----+------+
| c1 | c2   |
+----+------+
| -1 | ddd  |
|  2 | aaa  |
|  3 | bbb  |
|  4 | ccc  |
+----+------+
4 rows in set (0.00 sec) 

(root@localhost) [test] > SELECT auto_increment FROM information_schema.tables where table_schema='test' and table_name='t1';
+----------------+
| auto_increment |
+----------------+
|              5 |
+----------------+
1 row in set (0.00 sec)

自增列值大於列類型最大的整型值

無論 AUTO_INCREMENT 處於哪種鎖模式下,自增列分配的值如果大於自增列所屬欄位類型的最大值,則自增列分配值機制就不會生效。

測試:
在MySQL當中,INT類型的最大值為 -2147483648~2147483647

(root@localhost) [test] > select * from t1;
+----+------+
| c1 | c2   |
+----+------+
| -1 | ddd  |
|  2 | aaa  |
|  3 | bbb  |
|  4 | ccc  |
+----+------+
4 rows in set (0.00 sec)

(root@localhost) [test] > SELECT auto_increment FROM information_schema.tables where table_schema='test' and table_name='t1';
+----------------+
| auto_increment |
+----------------+
|              5 |
+----------------+
1 row in set (0.00 sec)

(root@localhost) [test] > insert into t1 values(2147483647,'eee');
Query OK, 1 row affected (0.02 sec)

(root@localhost) [test] > select * from t1;
+------------+------+
| c1         | c2   |
+------------+------+
|         -1 | ddd  |
|          2 | aaa  |
|          3 | bbb  |
|          4 | ccc  |
| 2147483647 | eee  |
+------------+------+
5 rows in set (0.00 sec)

(root@localhost) [test] > SELECT auto_increment FROM information_schema.tables where table_schema='test' and table_name='t1';
+----------------+
| auto_increment |
+----------------+
|     2147483647 |
+----------------+
1 row in set (0.00 sec)

可以看出自增列 AUTO_INCREMENT 並不會分配。

大量插入導致的間隙

  • 當參數值 innodb_autoinc_lock_mode 設置為 01 時,每條語句生成的自增列值都是連續的,不會產生間隙。因為表級 AUTO-INC 鎖會一直持有直到語句結束,並且同一時間只有一條語句在執行;
  • 當參數值 innodb_autoinc_lock_mode 設置為 2 時,在大量插入時有可能會產生間隙,但是只有當併發執行 INSERT 語句時。
  • 對於設置成 1 或者 2 情形下,在連續的語句之間可能產生間隙,因為對於大量插入InnoDB並不清楚每條語句所需自增量值數量。

混合模式插入自增列值分配

測試表:

-- t1表:表中無數據,但自增列下一個分配值從101開始
(root@localhost) [test] > show create table t1\G;
*************************** 1. row ***************************
       Table: t1
Create Table: CREATE TABLE `t1` (
  `c1` int(11) NOT NULL AUTO_INCREMENT,
  `c2` varchar(10) DEFAULT NULL,
  PRIMARY KEY (`c1`)
) ENGINE=InnoDB AUTO_INCREMENT=101 DEFAULT CHARSET=utf8
1 row in set (0.00 sec)

(root@localhost) [test] > select count(*) from t1;
+----------+
| count(*) |
+----------+
|        0 |
+----------+
1 row in set (0.00 sec)

-- t2表:表中有100萬行數據,並且自增列值是連續的
(root@localhost) [test] > show create table t2\G;
*************************** 1. row ***************************
       Table: t2
Create Table: CREATE TABLE `t2` (
  `c1` int(11) NOT NULL AUTO_INCREMENT,
  `c2` varchar(10) DEFAULT NULL,
  PRIMARY KEY (`c1`)
) ENGINE=InnoDB AUTO_INCREMENT=1000001 DEFAULT CHARSET=utf8
1 row in set (0.00 sec)

(root@localhost) [test] > select count(*) from t2;
+----------+
| count(*) |
+----------+
|  1000000 |
+----------+
1 row in set (0.22 sec)

(root@localhost) [test] > select min(c1),max(c1) from t2;
+---------+---------+
| min(c1) | max(c1) |
+---------+---------+
|       1 | 1000000 |
+---------+---------+
1 row in set (0.01 sec)

innodb_autoinc_lock_mode = 0

(root@localhost) [test] > show global variables like 'innodb_autoinc_lock_mode';
+--------------------------+-------+
| Variable_name            | Value |
+--------------------------+-------+
| innodb_autoinc_lock_mode | 0     |
+--------------------------+-------+
1 row in set (0.02 sec)

-- 1、開啟session 1執行TRX1
TRX1: insert into t1(c2) select c2 from t2;

(root@localhost) [test] > insert into t1(c2) select c2 from t2;
Query OK, 1000000 rows affected (6.37 sec)
Records: 1000000  Duplicates: 0  Warnings: 0

-- 2、在TRX1執行期間開啟session 2執行TRX2
TRX2: insert into t1 (c1,c2) values (1,'test_inc_a'), (NULL,'test_inc_b'), (5,'test_inc_c'), (0,'test_inc_d');

(root@localhost) [test] > insert into t1 (c1,c2) values (1,'test_inc_a'), (NULL,'test_inc_b'), (5,'test_inc_c'), (0,'test_inc_d');
Query OK, 4 rows affected (5.01 sec)
Records: 4  Duplicates: 0  Warnings: 0

-- 3、查看TRX2插入值的記錄
(root@localhost) [test] > select * from t1 where c2 in ('test_inc_a','test_inc_b','test_inc_c','test_inc_d');
+---------+------------+
| c1      | c2         |
+---------+------------+
|       1 | test_inc_a |
|       5 | test_inc_c |
| 1000101 | test_inc_b |
| 1000102 | test_inc_d |
+---------+------------+
4 rows in set (0.34 sec)

-- 4、查看當前AUTO_INCREMENT值

(root@localhost) [test] > select auto_increment from information_schema.tables where table_schema='test' and table_name='t1';
+----------------+
| auto_increment |
+----------------+
|        1000103 |
+----------------+
1 row in set (0.00 sec)
  • TRX1:持有了 AUTO-INC 表鎖,自動分配了101~1000100的自增值,並保證是連續的;
  • TRX2:需等待 AUTO-INC 鎖的釋放,自動分配了1000101~1000102的自增值。

可以看出下一個自增列值為 103,因為自增列的值是在每條插入語句執行時分配的,而不是一開始就分配完的。

innodb_autoinc_lock_mode = 1

(root@localhost) [test] > show global variables like 'innodb_autoinc_lock_mode';
+--------------------------+-------+
| Variable_name            | Value |
+--------------------------+-------+
| innodb_autoinc_lock_mode | 1     |
+--------------------------+-------+
1 row in set (0.01 sec)

-- 1、開啟session 1執行TRX1
TRX1: insert into t1(c2) select c2 from t2;

(root@localhost) [test] > insert into t1(c2) select c2 from t2;
Query OK, 1000000 rows affected (5.88 sec)
Records: 1000000  Duplicates: 0  Warnings: 0

-- 2、在TRX1執行期間開啟session 2執行TRX2
TRX2: insert into t1 (c1,c2) values (1,'test_inc_a'), (NULL,'test_inc_b'), (5,'test_inc_c'), (0,'test_inc_d');

(root@localhost) [test] > insert into t1 (c1,c2) values (1,'test_inc_a'), (NULL,'test_inc_b'), (5,'test_inc_c'), (0,'test_inc_d');
Query OK, 4 rows affected (4.38 sec)
Records: 4  Duplicates: 0  Warnings: 0

-- 3、查看TRX2插入值的記錄
(root@localhost) [test] > select * from t1 where c2 in ('test_inc_a','test_inc_b','test_inc_c','test_inc_d');
+---------+------------+
| c1      | c2         |
+---------+------------+
|       1 | test_inc_a |
|       5 | test_inc_c |
| 1048661 | test_inc_b |
| 1048662 | test_inc_d |
+---------+------------+
4 rows in set (0.32 sec)

-- 4、查看當前AUTO_INCREMENT值
(root@localhost) [test] > select auto_increment from information_schema.tables where table_schema='test' and table_name='t1';
+----------------+
| auto_increment |
+----------------+
|        1048665 |
+----------------+
1 row in set (0.00 sec)

-- 5、查看t1表的前10行記錄、後10行記錄和總行數
(root@localhost) [test] > select * from t1 order by c1 asc limit 10;
+-----+------------+
| c1  | c2         |
+-----+------------+
|   1 | test_inc_a |
|   5 | test_inc_c |
| 101 | CwAkHbEWs  |
| 102 | re         |
| 103 | uqrQbj     |
| 104 | SQ         |
| 105 | ojyPkMA    |
| 106 | 03qNqZ     |
| 107 | G8J        |
| 108 | Uo3        |
+-----+------------+
10 rows in set (0.00 sec)

(root@localhost) [test] > select * from t1 order by c1 asc limit 999994,10;
+---------+------------+
| c1      | c2         |
+---------+------------+
| 1000093 | o4AzuR     |
| 1000094 | NJMJJk     |
| 1000095 | 0o5xPuDnE  |
| 1000096 | QLLtImQC   |
| 1000097 | N1Fojm     |
| 1000098 | 6aZemarPC  |
| 1000099 | 4OUGSM1JzL |
| 1000100 | l8g6J      |
| 1048661 | test_inc_b |
| 1048662 | test_inc_d |
+---------+------------+
10 rows in set (0.32 sec)

(root@localhost) [test] > select count(*) from t1;
+----------+
| count(*) |
+----------+
|  1000004 |
+----------+
1 row in set (0.17 sec)

在此模式下:

  • TRX1:大量插入時持有 AUTO-INC 表鎖,自增列的值是預先分配的,101~10000100,總共100萬個連續值;
  • TRX2:混合插入情況下,語句為簡單 INSERT 語句,有的行自增列有指定值,而有的行沒有,這時TRX2是無須等待持有 AUTO-INC 鎖的,由於TRX1語句還在執行,InnoDB並不知道需要分配多少個自增列值,也不清楚TRX用了多少個自增列值,所以在並行執行 INSERT 時對於未指定行的自增列值分配就會產生間隙(1000100~1048661之間的間隙),但是語句當中分配的自增列(1048661和1048662)值依然是連續的。

可以看出下一個自增列值為 1048665 ,因為自增列值個數在語句執行開始就已經分配了4個(1048661~1048664),但實際語句只使用了2個。

innodb_autoinc_lock_mode = 2

(root@localhost) [test] > show global variables like 'innodb_autoinc_lock_mode';
+--------------------------+-------+
| Variable_name            | Value |
+--------------------------+-------+
| innodb_autoinc_lock_mode | 2     |
+--------------------------+-------+
1 row in set (0.01 sec)

-- 1、開啟session 1執行TRX1
TRX1: insert into t1(c2) select c2 from t2;

(root@localhost) [test] > insert into t1(c2) select c2 from t2;
Query OK, 1000000 rows affected (4.67 sec)
Records: 1000000  Duplicates: 0  Warnings: 0

-- 2、在TRX1執行期間開啟session 2執行TRX2
TRX2: insert into t1 (c1,c2) values (1,'test_inc_a'), (NULL,'test_inc_b'), (5,'test_inc_c'), (0,'test_inc_d');

(root@localhost) [test] > insert into t1 (c1,c2) values (1,'test_inc_a'), (NULL,'test_inc_b'), (5,'test_inc_c'), (0,'test_inc_d');
Query OK, 4 rows affected (0.02 sec)
Records: 4  Duplicates: 0  Warnings: 0

-- 3、查看TRX2插入值的記錄
(root@localhost) [test] > select * from t1 where c2 in ('test_inc_a','test_inc_b','test_inc_c','test_inc_d');
+--------+------------+
| c1     | c2         |
+--------+------------+
|      1 | test_inc_a |
|      5 | test_inc_c |
| 262241 | test_inc_b |
| 262242 | test_inc_d |
+--------+------------+
4 rows in set (0.28 sec)

-- 4、查看當前AUTO_INCREMENT值
(root@localhost) [test] > select auto_increment from information_schema.tables where table_schema='test' and table_name='t1';
+----------------+
| auto_increment |
+----------------+
|        1048665 |
+----------------+
1 row in set (0.00 sec)

-- 5、查看t1表的前10行記錄、後10行記錄和總行數
(root@localhost) [test] > select * from t1 order by c1 asc limit 10;
+-----+------------+
| c1  | c2         |
+-----+------------+
|   1 | test_inc_a |
|   5 | test_inc_c |
| 101 | CwAkHbEWs  |
| 102 | re         |
| 103 | uqrQbj     |
| 104 | SQ         |
| 105 | ojyPkMA    |
| 106 | 03qNqZ     |
| 107 | G8J        |
| 108 | Uo3        |
+-----+------------+
10 rows in set (0.00 sec)

(root@localhost) [test] > select * from t1 order by c1 asc limit 999994,10;
+---------+------------+
| c1      | c2         |
+---------+------------+
| 1000095 | KHukB      |
| 1000096 | bnpyaevl79 |
| 1000097 | o4AzuR     |
| 1000098 | NJMJJk     |
| 1000099 | 0o5xPuDnE  |
| 1000100 | QLLtImQC   |
| 1000101 | N1Fojm     |
| 1000102 | 6aZemarPC  |
| 1000103 | 4OUGSM1JzL |
| 1000104 | l8g6J      |
+---------+------------+
10 rows in set (0.22 sec)

(root@localhost) [test] > select count(*) from t1;
+----------+
| count(*) |
+----------+
|  1000004 |
+----------+
1 row in set (0.17 sec)

-- 6、查看TRX2插入時產生間隙位置行記錄
(root@localhost) [test] > select * from t1 where c1 between 262240 and 262250;
+--------+------------+
| c1     | c2         |
+--------+------------+
| 262240 | mNfx37     |
| 262241 | test_inc_b |
| 262242 | test_inc_d |
| 262245 | Taqi       |
| 262246 | Ojpakez    |
| 262247 | 2fKtmm6rW  |
| 262248 | AysE       |
| 262249 | wqWGs      |
| 262250 | lC         |
+--------+------------+
9 rows in set (0.00 sec)

在此模式下:

  • TRX1:因為大量插入時是不持有 AUTO-INC 表鎖,所在TRX1在執行期間,TRX2很快就插入完成並分配了自增列值。
  • TRX2:簡單插入語句時可以判斷出須分配4個自增列值,但是只使用了2個,這樣就造成了2個間隙(262243~262244)。

可以看出下一個自增列值為 1048665 ,TRX1執行時因為大量插入時無法預估插入值數量,TRX2執行時分配了4個自增值,但只使用了2個(262241~262242),造成了2個間隙,TRX1和TRX2的自增列值是交叉分配的。

修改自增列當中的值

無論 AUTO_INCREMENT 處於哪種鎖模式下,更新自增列的值都有可能會產生 Duplicate entry 重覆值錯誤。

-- 測試表:

(root@localhost) [test] > create table t3 (c1 int not null auto_increment primary key);
Query OK, 0 rows affected (0.06 sec)

(root@localhost) [test] > show create table t3\G;
*************************** 1. row ***************************
       Table: t3
Create Table: CREATE TABLE `t3` (
  `c1` int(11) NOT NULL AUTO_INCREMENT,
  PRIMARY KEY (`c1`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
1 row in set (0.00 sec)

-- 1、插入幾行數據
(root@localhost) [test] > insert into t3 values(0),(0),(3);
Query OK, 3 rows affected (0.04 sec)
Records: 3  Duplicates: 0  Warnings: 0

(root@localhost) [test] > select * from t3;
+----+
| c1 |
+----+
|  1 |
|  2 |
|  3 |
+----+
3 rows in set (0.00 sec)

(root@localhost) [test] > select auto_increment from information_schema.tables where table_schema='test' and table_name='t3';
+----------------+
| auto_increment |
+----------------+
|              4 |
+----------------+
1 row in set (0.00 sec)

-- 2、更新c1值為5
(root@localhost) [test] > update t3 set c1 = 5 where c1 = 3;
Query OK, 1 row affected (0.03 sec)
Rows matched: 1  Changed: 1  Warnings: 0

(root@localhost) [test] > select * from t3;
+----+
| c1 |
+----+
|  1 |
|  2 |
|  5 |
+----+
3 rows in set (0.00 sec)

(root@localhost) [test] > select auto_increment from information_schema.tables where table_schema='test' and table_name='t3';
+----------------+
| auto_increment |
+----------------+
|              4 |
+----------------+
1 row in set (0.00 sec)

-- 3、再次插入幾行數據
(root@localhost) [test] > insert into t3 values(0),(0),(3);
ERROR 1062 (23000): Duplicate entry '5' for key 'PRIMARY'

(root@localhost) [test] > select * from t3;
+----+
| c1 |
+----+
|  1 |
|  2 |
|  5 |
+----+
3 rows in set (0.00 sec)

可以看出更新了自增列的值之後產生了 Duplicate entry 重覆值錯誤。建議不要更新自增列的值,或是把自增列值更新為大於 AUTO_INCREMENT 的值。

AUTO_INCREMENT計數器的初始化

當為一張InnoDB表指定自增列時,此時表在數據字典中維護著一個特殊的計數器為自增列分配值,名稱為 auto_increment 計數器,這個計數器是存儲中記憶體中,而不是在磁碟上。

當伺服器重啟之後後,為了初始化 auto_increment 計數器,InnoDB執行如下等效語句來確定自增列下個需要分配的值:

SELECT MAX(ai_col) FROM table_name FOR UPDATE;

預設的,自增列的下一個分配值由以上語句得出的值增加 1 ,這個增加步長由參數 auto_increment_increment 決定。如果是空表,則自增列的下一個分配值為 1 ,這個空表時預設值由參數 auto_increment_offset 決定。

  • 當表中指定自增列,如果沒有為自增列值指定值,則由計數器分配。如果為自增列指定了值,還是插入指定的值。如果指定插入值大於計數器的值,同時計數器也會跟著調整,這樣容易造成間隙。
  • 由於計數據器是運行在記憶體中,所以當伺服器重啟後,InnoDB會重新初始化計數器。
  • 伺服器重啟的同時也重置了之前 CREATE TABLEALTER TABLE 為表指定的 AUTO_INCREMENT 值,可以再次通過語句進行指定。

參考

https://dev.mysql.com/doc/refman/5.7/en/innodb-auto-increment-handling.html

☆〖本人水平有限,文中如有錯誤還請留言批評指正!〗☆


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • CentOS 7查看以開放埠命令:firewall-cmd —list-ports 查看埠是否開放命令:第一個方法就是使用lsof -i:埠號命令行,例如lsof -i:80。如果沒有任何信息輸出,則表示該埠號(此處是80)沒有開放。第二個方法就是使用netstat -aptn命令行,查看所 ...
  • 在我們使用ARM等嵌入式Linux系統的時候,一個頭疼的問題是GPU,Camera,HDMI等都需要預留大量連續記憶體,這部分記憶體平時不用,但是一般的做法又必須先預留著。目前,Marek Szyprowski和Michal Nazarewicz實現了一套全新的Contiguous Memory All ...
  • 1 前景回顧 在內核初始化完成之後, 記憶體管理的責任就由伙伴系統來承擔. 伙伴系統基於一種相對簡單然而令人吃驚的強大演算法. Linux內核使用二進位伙伴演算法來管理和分配物理記憶體頁面, 該演算法由Knowlton設計, 後來Knuth又進行了更深刻的描述. 伙伴系統是一個結合了2的方冪個分配器和空閑緩衝 ...
  • 最近在ubuntu 12.04下安裝QT的過程中,遇到一個問題。 ./qt-opensource-linux-x64-5.7.0.run出現了bash: ./qt-opensource-linux-x64-5.7.0.run: cannot execute binary file。 我用我自己的解決 ...
  • 20181225 Linux Shell Bash環境下自動化創建ssh互信腳本 1. 我的Blog 博客園 https://www.cnblogs.com/piggybaba/ 個人網站 http://piggybaba.cn GitHub https://github.com/AndyYHM/W ...
  • 一、環境需求 本帖針對的是Linux環境,Windows或其他系統也可借鑒。具體只講述Jenkins配置以及整個流程的實現。 1.JDK(或JRE)及Java環境變數配置,我用的是JDK1.8.0_144,網上帖子也很多,不贅述。 2.Jenkins 持續集成和持續交付項目。 3.現有項目及gitl ...
  • 網址一:http://www.rpm-find.net/linux/rpm2html/search.php 網址二:http://rpmfind.net/linux/rpm2html/search.php 這兩個網址感覺一樣,但還是有差別的。基本都能搜索到你想要的包。 rpm安裝命令:http:// ...
  • 1 FTP工作模式 2 不同模式FTP面臨的問題 3 主動模式的FTP連接建立連接主要步驟 客戶端打開一個隨機的埠(埠號大於1024,在這裡,我們稱它為x),同時一個FTP進程連接至伺服器的21號命令埠。此時,源埠為隨機埠x(在客戶端);遠程埠為21(在伺服器端)。輸入正確的用戶名/密碼 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...