關於MYSQL 的 AUTO-INC Locks

今天看到一題MYSQL OCP 試題,關於 AUTO-INC locks ,才知在自增長鍵值時,是使用表鎖。

試題如下,同時把相關的資料也轉發如下。


QUESTION 94

Which two statements are true about InnoDB auto-increment locking?
A. The auto-increment lock can be a table-level lock.
B. InnoDB never uses table-level locks.
C. Some settings for innodb_autoinc_lock_mode can help reduce locking.
D. InnoDB always protects auto-increment updates with a table-level lock.
E. InnoDB does not use locks to enforce auto-increment uniqueness.
Correct Answer: AD
Section: (none)
Explanation
Explanation/Reference:
Explanation: A (not B): InnoDB uses a special lock called the table-level AUTO-INC lock for inserts into tables with AUTO_INCREMENT columns.
D (Not E): This lock is normally held to the end of the statement (not to the end of the transaction), to ensure that auto-increment numbers are assigned in a
predictable and repeatable order for a given sequence of INSERT statements.
Reference: 14.6.5.2 Configurable InnoDB Auto-Increment Locking

http://dev.mysql.com/doc/refman/5.6/en/innodb-auto-increment-configurable.html




AUTO-INC Locks

An AUTO-INC lock is a special table-level lock taken by transactions inserting into tables with AUTO_INCREMENT columns. In the simplest case, if one transaction is inserting values into the table, any other transactions must wait to do their own inserts into that table, so that rows inserted by the first transaction receive consecutive primary key values.

The innodb_autoinc_lock_mode configuration option controls the algorithm used for auto-increment locking. It allows you to choose how to trade off between predictable sequences of auto-increment values and maximum concurrency for insert operations.

AUTO-INC鎖是當向使用含有AUTO_INCREMENT列的表中插入數據時需要獲取的一種特殊的表級鎖 
在最簡單的情況下,如果一個事務正在向表中插入值,則任何其他事務必須等待對該表執行自己的插入操作,以便第一個事務插入的行的值是連續的。 
innodb_autoinc_lock_mode配置選項控制用於自動增量鎖定的算法。 它允許您選擇如何在可預測的自動遞增值序列和插入操作的最大併發性之間進行權衡。

AUTO_INCREMENT Handling in InnoDB

InnoDB提供了一個可配置的鎖定機制,可以顯着提高使用AUTO_INCREMENT列向表中添加行的SQL語句的可伸縮性和性能。 要對InnoDB表使用AUTO_INCREMENT機制,必須將AUTO_INCREMENT列定義爲索引的一部分,以便可以對錶執行相當於索引的SELECT MAX(ai_col)查找以獲取最大列值。 通常,這是通過使列成爲某些表索引的第一列來實現的。

本節介紹AUTO_INCREMENT鎖定模式的行爲,對不同AUTO_INCREMENT鎖定模式設置的使用含義,以及InnoDB如何初始化AUTO_INCREMENT計數器。

  • InnoDB AUTO_INCREMENT鎖定模式

  • InnoDB AUTO_INCREMENT鎖定模式使用含義

  • InnoDB AUTO_INCREMENT計數器初始化

InnoDB AUTO_INCREMENT鎖定模式

本節介紹用於生成自動遞增值的AUTO_INCREMENT鎖定模式的行爲,以及每種鎖定模式如何影響複製。 自動遞增鎖定模式在啓動時使用innodb_autoinc_lock_mode配置參數進行配置。

以下術語用於描述innodb_autoinc_lock_mode設置:

  • “INSERT-like” statements(類INSERT語句) 
    所有可以向表中增加行的語句,包括INSERTINSERT ... SELECTREPLACEREPLACE ... SELECT, and LOAD DATA.包括“simple-inserts”, “bulk-inserts”, and “mixed-mode” inserts.

  • “Simple inserts” 
    可以預先確定要插入的行數(當語句被初始處理時)的語句。 這包括沒有嵌套子查詢的單行和多行INSERT和REPLACE語句,但不包括INSERT ... ON DUPLICATE KEY UPDATE

  • “Bulk inserts” 
    事先不知道要插入的行數(和所需自動遞增值的數量)的語句。 這包括INSERT ... SELECTREPLACE ... SELECTLOAD DATA語句,但不包括純INSERT。 InnoDB在處理每行時一次爲AUTO_INCREMENT列分配一個新值。

  • “Mixed-mode inserts” 
    這些是“Simple inserts”語句但是指定一些(但不是全部)新行的自動遞增值。 示例如下,其中c1是表t1的AUTO_INCREMENT列: 
    INSERT INTO t1 (c1,c2) VALUES (1,'a'), (NULL,'b'), (5,'c'), (NULL,'d');

另一種類型的“Mixed-mode inserts”是INSERT ... ON DUPLICATE KEY UPDATE,其在最壞的情況下實際上是INSERT語句隨後又跟了一個UPDATE,其中AUTO_INCREMENT列的分配值不一定會在 UPDATE 階段使用

  • innodb_autoinc_lock_mode = 0 (“traditional” lock mode)

    傳統的鎖定模式提供了在MySQL 5.1中引入innodb_autoinc_lock_mode配置參數之前存在的相同行爲。傳統的鎖定模式選項用於向後兼容性,性能測試以及解決“Mixed-mode inserts”的問題,因爲語義上可能存在差異。

在此鎖定模式下,所有“INSERT-like”語句獲得一個特殊的表級AUTO-INC鎖,用於插入具有AUTO_INCREMENT列的表。此鎖定通常保持到語句結束(不是事務結束),以確保爲給定的INSERT語句序列以可預測和可重複的順序分配自動遞增值,並確保自動遞增由任何給定語句分配的值是連續的。

  1. SESSION_A>DROP TABLE IF EXISTS t;
  2. Query OK, 0 rows affected (0.00 sec)
  3. SESSION_A>CREATE TABLE t (a bigint unsigned auto_increment primary key) ENGINE=InnoDB;
  4. Query OK, 0 rows affected (0.01 sec)
  5. SESSION_A>insert into t values(1),(3),(4),(5),(6),(7);
  6. Query OK, 6 rows affected (0.01 sec)
  7. Records: 6 Duplicates: 0 Warnings: 0
  8. SESSION_A>select * from t;
  9. +---+
  10. | a |
  11. +---+
  12. | 1 |
  13. | 3 |
  14. | 4 |
  15. | 5 |
  16. | 6 |
  17. | 7 |
  18. +---+
  19. 6 rows in set (0.00 sec)
  20. SESSION_A>select @@innodb_autoinc_lock_mode;
  21. +----------------------------+
  22. | @@innodb_autoinc_lock_mode |
  23. +----------------------------+
  24. | 0 |
  25. +----------------------------+
  26. 1 row in set (0.00 sec)
  27. A B C 三個會話事務隔離級別都是 RR
  28. SESSION_A>select @@global.tx_isolation,@@session.tx_isolation;
  29. +-----------------------+------------------------+
  30. | @@global.tx_isolation | @@session.tx_isolation |
  31. +-----------------------+------------------------+
  32. | REPEATABLE-READ | REPEATABLE-READ |
  33. +-----------------------+------------------------+
  34. SESSION_A>begin;
  35. Query OK, 0 rows affected (0.00 sec)
  36. SESSION_A>delete from t where a>4;
  37. Query OK, 3 rows affected (0.00 sec)
  38. B會話被鎖,這是由於會話 A 產生的 gap lock
  39. SESSION_B>begin;
  40. Query OK, 0 rows affected (0.00 sec)
  41. SESSION_B>insert into t values(null); --注意這裏因爲是 null, 鎖需要在內存中分配 AUTO-INCREMENT
  42. C 會話被阻塞
  43. SESSION_C>begin;
  44. Query OK, 0 rows affected (0.00 sec)
  45. SESSION_C>insert into t values(2); --這裏插入2,沒有 gap lock 也被鎖了
  46. (mysql@localhost) [fandb]> (mysql@localhost) [fandb]> select trx_id,trx_state,trx_requested_lock_id,trx_weight,trx_mysql_thread_id,trx_query, trx_operation_state from information_schema.INNODB_TRX;
  47. +--------+-----------+-----------------------+------------+---------------------+----------------------------+-----------------------+
  48. | trx_id | trx_state | trx_requested_lock_id | trx_weight | trx_mysql_thread_id | trx_query | trx_operation_state |
  49. +--------+-----------+-----------------------+------------+---------------------+----------------------------+-----------------------+
  50. | 321912 | LOCK WAIT | 321912:701 | 3 | 7 | insert into t values(2) | setting auto-inc lock |
  51. | 321911 | LOCK WAIT | 321911:690:3:1 | 3 | 2 | insert into t values(null) | inserting |
  52. | 321906 | RUNNING | NULL | 5 | 1 | NULL | NULL |
  53. +--------+-----------+-----------------------+------------+---------------------+----------------------------+-----------------------+
  54. 3 rows in set (0.00 sec)
  55. 可以看到,SESSION_C是等待自增鎖,一直處於setting auto-inc lock狀態
  56. (mysql@localhost) [fandb]> select * from information_schema.INNODB_LOCKS;
  57. +----------------+-------------+-----------+-----------+-------------+------------+------------+-----------+----------+------------------------+
  58. | lock_id | lock_trx_id | lock_mode | lock_type | lock_table | lock_index | lock_space | lock_page | lock_rec | lock_data |
  59. +----------------+-------------+-----------+-----------+-------------+------------+------------+-----------+----------+------------------------+
  60. | 321912:701 | 321912 | AUTO_INC | TABLE | `fandb`.`t` | NULL | NULL | NULL | NULL | NULL |
  61. | 321911:701 | 321911 | AUTO_INC | TABLE | `fandb`.`t` | NULL | NULL | NULL | NULL | NULL |
  62. | 321911:690:3:1 | 321911 | X | RECORD | `fandb`.`t` | PRIMARY | 690 | 3 | 1 | supremum pseudo-record |
  63. | 321906:690:3:1 | 321906 | X | RECORD | `fandb`.`t` | PRIMARY | 690 | 3 | 1 | supremum pseudo-record |
  64. +----------------+-------------+-----------+-----------+-------------+------------+------------+-----------+----------+------------------------+
  65. 4 rows in set (0.00 sec)

在statement-based replication的情況下,這意味着當在從服務器上覆制SQL語句時,自動增量列使用與主服務器上相同的值。多個INSERT語句的執行結果是確定性的,SLAVE再現與MASTER相同的數據。如果由多個INSERT語句生成的自動遞增值交錯,則兩個併發INSERT語句的結果將是不確定的,並且不能使用基於語句的複製可靠地傳播到從屬服務器。

爲了解釋清楚,查看下面的例子:

  1. CREATE TABLE t1 (
  2. c1 INT(11) NOT NULL AUTO_INCREMENT,
  3. c2 VARCHAR(10) DEFAULT NULL,
  4. PRIMARY KEY (c1)
  5. ) ENGINE=InnoDB;

假設有兩個事務正在運行,每個事務都將行插入到具有AUTO_INCREMENT列的表中。 一個事務正在使用插入1000行的INSERT … SELECT語句,另一個事務正在使用插入一行的“Simple inserts”語句:

  1. Tx1: INSERT INTO t1 (c2) SELECT 1000 rows from another table ...
  2. Tx2: INSERT INTO t1 (c2) VALUES ('xxx');

InnoDB不能預先得知有多少行會從TX1的select部分獲取到,所以在事務進行過程中,InnoDB一次只會爲AUTO_INCREMENT列分配一個值. 
通過一個表級鎖的控制,保證了在同一時刻只有一個引用表t1的INSERT語句可以執行,直到整個INSERT語句結束,並且由不同語句生成自動遞增數不會交錯 
由Tx1 INSERT ... SELECT語句生成的自動遞增值將是連續的,並且Tx2中的INSERT語句使用的(單個)自動遞增值將小於或大於用於Tx1的所有那些值,具體取決於 那個語句先執行。

只要SQL語句在從二進制日誌(當使用基於語句的複製或在恢復方案中)重放時以相同的順序執行,結果將與Tx1和Tx2首次運行時的結果相同。 因此,持續至語句結束的表級鎖定( table-level locks)保證了在statement-based replication中對auto-increment列的插入數據的安全性. 但是,當多個事務同時執行insert語句時,這些表級鎖定會限制併發性和可伸縮性。

在前面的示例中,如果沒有表級鎖,則Tx2中用於INSERT的自動遞增列的值取決於語句執行的確切時間。 如果Tx2的INSERT在Tx1的INSERT正在運行時(而不是在它開始之前或完成之後)執行,則由兩個INSERT語句分配的特定自動遞增值將是不確定的,並且可能每次運行都會得到不同的值

在連續鎖定模式下,InnoDB可以避免爲“Simple inserts”語句使用表級AUTO-INC鎖,其中行數是預先已知的,並且仍然保留基於語句的複製的確定性執行和安全性。

如果不使用二進制日誌作爲恢復或複製的一部分來重放SQL語句,則可以使用interleaved lock模式來消除所有使用表級AUTO-INC鎖,以實現更大的併發性和性能,其代價是由於併發的語句交錯執行,同一語句生成的AUTO-INCREMENT值可能會產生GAP

  • innodb_autoinc_lock_mode = 1 (“consecutive” lock mode)

    這是默認的鎖定模式.在這個模式下,“bulk inserts”仍然使用AUTO-INC表級鎖,並保持到語句結束.這適用於所有INSERT ... SELECTREPLACE ... SELECTLOAD DATA語句。同一時刻只有一個語句可以持有AUTO-INC鎖.

“Simple inserts”(要插入的行數事先已知)通過在mutex(輕量鎖)的控制下獲得所需數量的自動遞增值來避免表級AUTO-INC鎖, 它只在分配過程的持續時間內保持,而不是直到語句完成。 不使用表級AUTO-INC鎖,除非AUTO-INC鎖由另一個事務保持。 如果另一個事務保持AUTO-INC鎖,則“簡單插入”等待AUTO-INC鎖,如同它是一個“批量插入”。

  1. SESSION_A>DROP TABLE IF EXISTS t;
  2. Query OK, 0 rows affected (0.01 sec)
  3. SESSION_A>CREATE TABLE t (a bigint unsigned auto_increment primary key) ENGINE=InnoDB;
  4. Query OK, 0 rows affected (0.01 sec)
  5. SESSION_A>insert into t values(1),(3),(4),(5),(6),(7);
  6. Query OK, 6 rows affected (0.01 sec)
  7. Records: 6 Duplicates: 0 Warnings: 0
  8. SESSION_A>select @@innodb_autoinc_lock_mode;
  9. +----------------------------+
  10. | @@innodb_autoinc_lock_mode |
  11. +----------------------------+
  12. | 1 |
  13. +----------------------------+
  14. 1 row in set (0.00 sec)
  15. SESSION_A>select * from t;
  16. +---+
  17. | a |
  18. +---+
  19. | 1 |
  20. | 3 |
  21. | 4 |
  22. | 5 |
  23. | 6 |
  24. | 7 |
  25. +---+
  26. 6 rows in set (0.00 sec)
  27. SESSION_A>begin;
  28. Query OK, 0 rows affected (0.00 sec)
  29. SESSION_A>delete from t where a>4;
  30. Query OK, 3 rows affected (0.00 sec)
  31. 會話 B, GAP LOCK 阻塞
  32. SESSION_B>begin;
  33. Query OK, 0 rows affected (0.00 sec)
  34. SESSION_B>insert into t values(null); --由於是`simple-insert``innodb_autoinc_lock_mode=1`,所以並不需要AUTO-INC表級鎖
  35. 會話 C 成功插入沒有阻塞
  36. SESSION_C>begin;
  37. Query OK, 0 rows affected (0.00 sec)
  38. SESSION_C>insert into t values(2); --由於它也是`simple-insert``innodb_autoinc_lock_mode=1`所以不需要獲取AUTO-INC表級鎖,沒有阻塞成功插入
  39. Query OK, 1 row affected (0.00 sec)
  40. C會話rollback,B會話改爲使用“Bulk inserts
  41. SESSION_C>rollback;
  42. Query OK, 0 rows affected (0.00 sec)
  43. SESSION_B>insert into t select null;
  44. 此時 C 會話又被阻塞了
  45. SESSION_C>begin;
  46. Query OK, 0 rows affected (0.00 sec)
  47. SESSION_C>insert into t values(2); --這驗證了官方文檔中的說法`If another transaction holds an AUTO-INC lock, a “simple insert” waits for the AUTO-INC lock, as if it were a “bulk insert”.`
  48. Query OK, 1 row affected (41.17 sec)

此鎖定模式確保,當行數不預先知道的INSERT存在時(並且自動遞增值在語句過程執行中分配)由任何“類INSERT”語句分配的所有自動遞增值是連續的,並且對於基於語句的複製(statement-based replication)操作是安全的。

這種鎖定模式顯著地提高了可擴展性,並且保證了對於基於語句的複製(statement-based replication)的安全性.此外,與“傳統”鎖定模式一樣,由任何給定語句分配的自動遞增數字是連續的。 與使用自動遞增的任何語句的“傳統”模式相比,語義沒有變化. 
但有一個特例:

The exception is for “mixed-mode inserts”, where the user provides explicit values for an AUTO_INCREMENT column for some, but not all, rows in a multiple-row “simple insert”. For such inserts, InnoDB allocates more auto-increment values than the number of rows to be inserted. However, all values automatically assigned are consecutively generated (and thus higher than) the auto-increment value generated by the most recently executed previous statement. “Excess” numbers are lost.

  • innodb_autoinc_lock_mode = 2 (“interleaved” lock mode)

    在這種鎖定模式下,所有類INSERT(“INSERT-like” )語句都不會使用表級AUTO-INC lock,並且可以同時執行多個語句。這是最快和最可擴展的鎖定模式,但是當使用基於語句的複製或恢復方案時,從二進制日誌重播SQL語句時,這是不安全的。

在此鎖定模式下,自動遞增值保證在所有併發執行的“類INSERT”語句中是唯一且單調遞增的。但是,由於多個語句可以同時生成數字(即,跨語句交叉編號),爲任何給定語句插入的行生成的值可能不是連續的。

如果執行的語句是“simple inserts”,其中要插入的行數已提前知道,則除了“混合模式插入”之外,爲單個語句生成的數字不會有間隙。然而,當執行“批量插入”時,在由任何給定語句分配的自動遞增值中可能存在間隙。

InnoDB AUTO_INCREMENT鎖定模式使用含義

  • 在複製環節中使用自增列 
    如果你在使用基於語句的複製(statement-based replication)請將innodb_autoinc_lock_mode設置爲0或1,並在主從上使用相同的值。 如果使用innodb_autoinc_lock_mode = 2(“interleaved”)或主從不使用相同的鎖定模式的配置,自動遞增值不能保證在從機上與主機上相同。

如果使用基於行的或混合模式的複製,則所有自動增量鎖定模式都是安全的,因爲基於行的複製對SQL語句的執行順序不敏感(混合模式會在遇到不安全的語句是使用基於行的複製模式)。

  • “Lost” auto-increment values and sequence gaps 
    在所有鎖定模式(0,1和2)中,如果生成自動遞增值的事務回滾,那些自動遞增值將“丟失”。 一旦爲自動增量列生成了值,無論是否完成“類似INSERT”語句以及包含事務是否回滾,都不能回滾。 這種丟失的值不被重用。 因此,存儲在表的AUTO_INCREMENT列中的值可能存在間隙。

  • Specifying NULL or 0 for the AUTO_INCREMENT column 
    在所有鎖定模式(0,1和2)中,如果用戶在INSERT中爲AUTO_INCREMENT列指定NULL或0,InnoDB會將該行視爲未指定值,併爲其生成新值。

  • 爲AUTO_INCREMENT列分配一個負值 
    在所有鎖定模式(0,1和2)中,如果您爲AUTO_INCREMENT列分配了一個負值,則不會定義自動增量機制的行爲。

  • 如果AUTO_INCREMENT值大於指定整數類型的最大整數 
    在所有鎖定模式(0,1和2)中,如果值大於可以存儲在指定整數類型中的最大整數,則不定義自動遞增機制的行爲。

  • Gaps in auto-increment values for “bulk inserts” 
    當innodb_autoinc_lock_mode設置爲0(“traditional”)或1(“consecutive”)時,任何給定語句生成的自動遞增值是連續的,沒有間隙,因爲表級AUTO-INC鎖會持續到 語句結束,並且一次只能執行一個這樣的語句。

當innodb_autoinc_lock_mode設置爲2(“interleaved”)時,在“bulk inserts”生成的自動遞增值中可能存在間隙,但只有在併發執行“INSERT-Like”語句時纔會產生這種情況。

對於鎖定模式1或2,在連續語句之間可能出現間隙,因爲對於批量插入,每個語句所需的自動遞增值的確切數目可能不爲人所知,並且可能進行過度估計。

  1. select @@innodb_autoinc_lock_mode;
  2. +----------------------------+
  3. | @@innodb_autoinc_lock_mode |
  4. +----------------------------+
  5. | 0 |
  6. +----------------------------+
  7. DROP TABLE IF EXISTS t;
  8. CREATE TABLE t (a bigint unsigned auto_increment primary key) ENGINE=InnoDB SELECT NULL AS a;
  9. /* #1 */ INSERT INTO t SELECT NULL FROM t;
  10. /* #2 */ INSERT INTO t SELECT NULL FROM t;
  11. /* #3 */ INSERT INTO t SELECT NULL FROM t;
  12. /* #4 */ INSERT INTO t SELECT NULL FROM t;
  13. SELECT * FROM t;
  14. +----+
  15. | a |
  16. +----+
  17. | 1 |
  18. | 2 |
  19. | 3 |
  20. | 4 |
  21. | 5 |
  22. | 6 |
  23. | 7 |
  24. | 8 |
  25. | 9 |
  26. | 10 |
  27. | 11 |
  28. | 12 |
  29. | 13 |
  30. | 14 |
  31. | 15 |
  32. | 16 |
  33. +----+
  34. innodb_autoinc_lock_mode=0 INSERT語句產生的自動遞增值都是連續的
  35. select @@innodb_autoinc_lock_mode;
  36. +----------------------------+
  37. | @@innodb_autoinc_lock_mode |
  38. +----------------------------+
  39. | 1 |
  40. +----------------------------+
  41. 1 row in set (0.00 sec)
  42. DROP TABLE IF EXISTS t;
  43. CREATE TABLE t (a bigint unsigned auto_increment primary key) ENGINE=InnoDB SELECT NULL AS a;
  44. /* #1 */ INSERT INTO t SELECT NULL FROM t;
  45. /* #2 */ INSERT INTO t SELECT NULL FROM t;
  46. /* #3 */ INSERT INTO t SELECT NULL FROM t;
  47. /* #4 */ INSERT INTO t SELECT NULL FROM t;
  48. SELECT * FROM t;
  49. +----+
  50. | a |
  51. +----+
  52. | 1 |
  53. | 2 |
  54. | 3 |
  55. | 4 |
  56. | 6 |
  57. | 7 |
  58. | 8 |
  59. | 9 |
  60. | 13 |
  61. | 14 |
  62. | 15 |
  63. | 16 |
  64. | 17 |
  65. | 18 |
  66. | 19 |
  67. | 20 |
  68. +----+
  69. 出現了間隙gap, 510-12都沒了,下面來解釋產生這種情況的原因:
  70. /* #1 */ 這是第一次INSERT,此時表中只有一行(創建表時的那一行),但是MySQL不知道有多少行.
  71. 然後MySQL Grab a chunk of auto_increment values chunk中有多少? 只有一個,即'2',將其插入表中.
  72. 沒有更多的行插入,所以一切完成。
  73. /* #2 */ 這是第二次INSERT,此時表中有兩行(1,2),但是MySQL不知道有多少行.
  74. MySQL Grab a chunk of auto_increment values chunk中有多少? 只有一個,即'3',將其插入表中.
  75. 還有需要插入的行,所以Grab another chunk,這次是前一次的兩倍大小 chunk中有多少? 兩個,'4''5'. 插入'4'.
  76. 沒有更多的行插入,所以一切完成,'5'被捨棄,但是此時 AUTO_INCREMENT的下一個值是6
  77. /* #3 */這是第三次INSERT,此時表中有四行(1,2,3,4),但是MySQL不知道有多少行.
  78. - Grab a chunk of auto_increment values. How many in the chunk? One - the value '6'. Insert it (one row inserted).
  79. - Still more rows to insert. Grab another chunk, twice as big as before - two values, '7' and '8'. Insert them (three rows inserted).
  80. - Still more rows to insert. Grab another chunk, twice as big as before - four values, '9', '10', '11', '12'. Insert the '9' (four rows inserted).
  81. - No more rows to insert. Discard the left over '10', '11', and '12'.
  82. #4: Insert as many rows as there are in the table (it's eight rows, but MySQL doesn't know that.)
  83. - Grab a chunk of auto_increment values. How many in the chunk? One - the value '13'. Insert it (one row inserted).
  84. - Still more rows to insert. Grab another chunk, twice as big as before - two values, '14' and '15'. Insert them (three rows inserted).
  85. - Still more rows to insert. Grab another chunk, twice as big as before - four values, '16', '17', '18', '19'. Insert them (seven rows inserted).
  86. - Still more rows to insert. Grab another chunk, twice as big as before - eight values, '20', '21', '22', ..., '27'. Insert the '20' (eight rows inserted).
  87. - No more rows to insert. Discard the left over '21', '22', etc.
  88. 所以這就是 gap 產生的原因
  • 由“mixed-mode inserts”分配的自動遞增值 
    考慮一下場景,在“mixed-mode insert”中,其中一個“simple insert”語句指定了一些(但不是全部)行的AUTO-INCREMENT值。 這樣的語句在鎖模式0,1和2中表現不同。例如,假設c1是表t1的AUTO_INCREMENT列,並且最近自動生成的序列號是100。
  1. mysql> CREATE TABLE t1 (
  2. -> c1 INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
  3. -> c2 CHAR(1)
  4. -> ) ENGINE = INNODB;

Now, consider the following “mixed-mode insert” statement:

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

當innodb_autoinc_lock_mode=0時:

  1. mysql> SELECT c1, c2 FROM t1 ORDER BY c2;
  2. +-----+------+
  3. | c1 | c2 |
  4. +-----+------+
  5. | 1 | a |
  6. | 101 | b |
  7. | 5 | c |
  8. | 102 | d |
  9. +-----+------+

下一個可用的auto-increment值103.因爲innodb_autoinc_lock_mode=0時,auto-increment值一次只分配一個,而不是在開始時全部分配.不論是否有併發的其他類INSERT語句同時執行,都會是這樣的結果

當innodb_autoinc_lock_mode=1時:

  1. mysql> SELECT c1, c2 FROM t1 ORDER BY c2;
  2. +-----+------+
  3. | c1 | c2 |
  4. +-----+------+
  5. | 1 | a |
  6. | 101 | b |
  7. | 5 | c |
  8. | 102 | d |
  9. +-----+------+

不同於innodb_autoinc_lock_mode=0時的情況,此時下一個可用的auto-increment值105,因爲auto-increment值在語句一開始就分配了,分配了四個,但是隻用了倆.不論是否有併發的其他類INSERT語句同時執行,都會是這樣的結果

當innodb_autoinc_lock_mode=2時:

  1. mysql> SELECT c1, c2 FROM t1 ORDER BY c2;
  2. +-----+------+
  3. | c1 | c2 |
  4. +-----+------+
  5. | 1 | a |
  6. | x | b |
  7. | 5 | c |
  8. | y | d |
  9. +-----+------+

x和y的值是唯一的,並大於任何先前生成的行。 然而,x和y的具體值取決於通過併發執行語句生成的自動增量值的數量。

最後考慮下面的情況,當最近的 AUTO-INCREMENT 值爲4時,執行下面的語句:

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

無論innodb_autoinc_lock_mode如何設置,都會報錯duplicate-key error 23000 (Can't write; duplicate key in table) 
因爲5已經分配給了(NULL, 'b'),所以導致插入(5, 'C')時報錯

  • 在INSERT語句序列的中間修改AUTO_INCREMENT列值 
    在所有鎖定模式(0,1和2)中,在INSERT語句序列中間修改AUTO_INCREMENT列值可能會導致duplicate key錯誤。
  1. mysql> CREATE TABLE t1 (
  2. -> c1 INT NOT NULL AUTO_INCREMENT,
  3. -> PRIMARY KEY (c1)
  4. -> ) ENGINE = InnoDB;
  5. mysql> INSERT INTO t1 VALUES(0), (0), (3); -- 0 0分配兩個值1,2. 手動指定3,則此時AUTO_INCREMENT3,下一個值爲4
  6. mysql> SELECT c1 FROM t1;
  7. +----+
  8. | c1 |
  9. +----+
  10. | 1 |
  11. | 2 |
  12. | 3 |
  13. +----+
  14. mysql> UPDATE t1 SET c1 = 4 WHERE c1 = 1;
  15. mysql> SELECT c1 FROM t1;
  16. +----+
  17. | c1 |
  18. +----+
  19. | 2 |
  20. | 3 |
  21. | 4 |
  22. +----+
  23. mysql> INSERT INTO t1 VALUES(0); --由於分配值爲4,所以報錯duplicate key
  24. ERROR 1062 (23000): Duplicate entry '4' for key 'PRIMARY'

InnoDB AUTO_INCREMENT計數器初始化

本章節討論 InnoDB如何初始化AUTO_INCREMENT計數器 
如果你爲一個Innodb表創建了一個AUTO_INCREMENT列,則InnoDB數據字典中的表句柄包含一個稱爲自動遞增計數器的特殊計數器,用於爲列分配新值。 此計數器僅存在於內存中,而不存儲在磁盤上。

要在服務器重新啓動後初始化自動遞增計數器,InnoDB將在首次插入行到包含AUTO_INCREMENT列的表時執行以下語句的等效語句。

  1. SELECT MAX(ai_col) FROM table_name FOR UPDATE;

InnoDB增加語句檢索的值,並將其分配給表和表的自動遞增計數器。 默認情況下,值增加1.此默認值可以由auto_increment_increment配置設置覆蓋。

如果表爲空,InnoDB使用值1.此默認值可以由auto_increment_offset配置設置覆蓋。

如果在自動遞增計數器初始化前使用SHOW TABLE STATUS語句查看錶, InnoDB將初始化計數器值,但不會遞增該值.這個值會儲存起來以備之後的插入語句使用.這個初始化過程使用了一個普通的排它鎖來讀取表中自增列的最大值. InnoDB遵循相同的過程來初始化新創建的表的自動遞增計數器。

在自動遞增計數器初始化之後,如果您未明確指定AUTO_INCREMENT列的值,InnoDB會遞增計數器並將新值分配給該列。如果插入顯式指定列值的行,並且該值大於當前計數器值,則將計數器設置爲指定的列值。

只要服務器運行,InnoDB就使用內存中自動遞增計數器。當服務器停止並重新啓動時,InnoDB會重新初始化每個表的計數器,以便對錶進行第一次INSERT,如前所述。

服務器重新啓動還會取消CREATE TABLE和ALTER TABLE語句中的AUTO_INCREMENT = N表選項的效果

(轉自: http://blog.csdn.net/ashic/article/details/53810319)


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章