文件首頁
MySQL 9.0 參考手冊
相關文件 下載本手冊
PDF (美式信紙) - 40.0Mb
PDF (A4) - 40.1Mb
Man Pages (TGZ) - 258.2Kb
Man Pages (Zip) - 365.3Kb
Info (Gzip) - 4.0Mb
Info (Zip) - 4.0Mb


MySQL 9.0 參考手冊  /  ...  /  InnoDB 中的 AUTO_INCREMENT 處理

17.6.1.6 InnoDB 中的 AUTO_INCREMENT 處理

InnoDB 提供可設定的鎖定機制,可大幅改善將列新增至具有 AUTO_INCREMENT 資料行之表格的 SQL 陳述式的可擴展性和效能。若要搭配 InnoDB 表格使用 AUTO_INCREMENT 機制,AUTO_INCREMENT 資料行必須定義為某個索引的第一個或唯一資料行,使其能夠對表格執行相當於索引 SELECT MAX(ai_col) 的查閱,以取得最大的資料行值。索引不一定要是 PRIMARY KEYUNIQUE,但為了避免 AUTO_INCREMENT 資料行中出現重複值,建議使用這些索引類型。

本節說明 AUTO_INCREMENT 鎖定模式、不同 AUTO_INCREMENT 鎖定模式設定的使用影響,以及 InnoDB 如何初始化 AUTO_INCREMENT 計數器。

InnoDB AUTO_INCREMENT 鎖定模式

本節說明用於產生自動遞增值的 AUTO_INCREMENT 鎖定模式,以及每個鎖定模式如何影響複寫。自動遞增鎖定模式在啟動時使用 innodb_autoinc_lock_mode 變數進行設定。

下列術語用於描述 innodb_autoinc_lock_mode 設定

  • INSERT 類似 陳述式

    在表格中產生新列的所有陳述式,包括 INSERTINSERT ... SELECTREPLACEREPLACE ... SELECTLOAD DATA。包括 簡單插入大量插入混合模式 插入。

  • 簡單插入

    可以預先判斷要插入的列數(在初始處理陳述式時)的陳述式。這包括不具有巢狀子查詢的單列和多列 INSERTREPLACE 陳述式,但不包括 INSERT ... ON DUPLICATE KEY UPDATE

  • 大量插入

    無法預先知道要插入的列數(和所需的自動遞增值數量)的陳述式。這包括 INSERT ... SELECTREPLACE ... SELECTLOAD DATA 陳述式,但不包括一般的 INSERTInnoDB 會在處理每一列時一次指派一個 AUTO_INCREMENT 資料行的新值。

  • 混合模式插入

    這些是為某些(但非全部)新列指定自動遞增值的 簡單插入 陳述式。以下範例中,c1 是表格 t1AUTO_INCREMENT 資料行

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

    另一種混合模式插入的類型是INSERT ... ON DUPLICATE KEY UPDATE,在最壞的情況下,它實際上是一個INSERT 接著一個UPDATE,其中 AUTO_INCREMENT 欄位分配的值在更新階段可能會或可能不會被使用。

對於 innodb_autoinc_lock_mode 變數,有三種可能的設定。這些設定值分別為 0、1 或 2,對應於傳統連續交錯鎖定模式。交錯鎖定模式 (innodb_autoinc_lock_mode=2) 是預設值。

MySQL 9.0 中交錯鎖定模式的預設設定反映了從基於語句的複製變更為基於列的複製作為預設複製類型。基於語句的複製需要連續的自動遞增鎖定模式,以確保對於給定的 SQL 語句序列,自動遞增值以可預測且可重複的順序分配,而基於列的複製則對 SQL 語句的執行順序不敏感。

  • innodb_autoinc_lock_mode = 0 (傳統鎖定模式)

    傳統鎖定模式提供與引入 innodb_autoinc_lock_mode 變數之前相同的行為。提供傳統鎖定模式選項是為了向後相容性、效能測試以及解決由於語義可能存在差異而導致的「混合模式插入」問題。

    在此鎖定模式下,所有類似 INSERT的語句都會取得一個特殊的表級 AUTO-INC 鎖,以將資料插入具有 AUTO_INCREMENT 欄位的表格中。這個鎖通常會持有到語句結束 (而不是交易結束),以確保對於給定的 INSERT 語句序列,自動遞增值以可預測且可重複的順序分配,並確保任何給定語句所分配的自動遞增值是連續的。

    就基於語句的複製而言,這表示當在副本伺服器上複製 SQL 語句時,自動遞增欄位會使用與來源伺服器上相同的值。多個 INSERT 語句的執行結果是確定的,並且副本會重現與來源相同的資料。如果多個 INSERT 語句產生的自動遞增值是交錯的,則兩個並行 INSERT 語句的結果將是不確定的,並且無法可靠地使用基於語句的複製傳播到副本伺服器。

    為了清楚說明,請考慮一個使用這個表格的範例

    CREATE TABLE t1 (
      c1 INT(11) NOT NULL AUTO_INCREMENT,
      c2 VARCHAR(10) DEFAULT NULL,
      PRIMARY KEY (c1)
    ) ENGINE=InnoDB;

    假設有兩個交易正在執行,每個交易都將資料列插入具有 AUTO_INCREMENT 欄位的表格中。一個交易正在使用 INSERT ... SELECT 語句來插入 1000 個資料列,另一個交易正在使用一個簡單的 INSERT 語句來插入一個資料列

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

    InnoDB 無法預先知道從 Tx1 的 INSERT 語句中的 SELECT 中檢索多少個資料列,並且它會在語句進行時一次分配一個自動遞增值。透過表級鎖定 (持有到語句結束),一次只能執行一個引用表格 t1INSERT 語句,並且不同語句產生的自動遞增編號不會交錯。Tx1 INSERT ... SELECT 語句產生的自動遞增值是連續的,並且 Tx2 中的 INSERT 語句使用的 (單個) 自動遞增值,會小於或大於所有 Tx1 使用的值,具體取決於哪個語句先執行。

    只要 SQL 語句在從二進位日誌重播時以相同的順序執行 (當使用基於語句的複製或在復原情況下),結果就會與 Tx1 和 Tx2 首次執行時相同。因此,持有到語句結束的表級鎖定使得使用自動遞增的 INSERT 語句對於基於語句的複製來說是安全的。然而,當多個交易同時執行插入語句時,這些表級鎖定會限制並行性和可擴展性。

    在前面的範例中,如果沒有表級鎖定,則 Tx2 中 INSERT 所使用的自動遞增欄位的值取決於語句的執行時間。如果 Tx2 的 INSERT 在 Tx1 的 INSERT 執行時執行 (而不是在其開始之前或完成之後),則兩個 INSERT 語句分配的特定自動遞增值是不確定的,並且可能會因執行而異。

    連續鎖定模式下,InnoDB 可以避免對事先已知資料列數的簡單插入語句使用表級 AUTO-INC 鎖,並且仍然保持確定性的執行和基於語句的複製安全性。

    如果您沒有使用二進位日誌來重播 SQL 語句作為復原或複製的一部分,則可以使用交錯鎖定模式來消除所有表級 AUTO-INC 鎖的使用,以獲得更高的並行性和效能,但代價是允許語句分配的自動遞增編號中存在間隙,並且可能使並行執行的語句分配的編號交錯。

  • innodb_autoinc_lock_mode = 1 (連續鎖定模式)

    在此模式下,大量插入會使用特殊的 AUTO-INC 表級鎖定,並將其持有到語句結束。這適用於所有 INSERT ... SELECTREPLACE ... SELECTLOAD DATA 語句。一次只能執行一個持有 AUTO-INC 鎖的語句。如果大量插入操作的來源表格與目標表格不同,則在來源表格所選取的第一列取得共享鎖定之後,會取得目標表格上的 AUTO-INC 鎖。如果大量插入操作的來源和目標是同一個表格,則會在選取的所有資料列上取得共享鎖定之後,取得 AUTO-INC 鎖。

    簡單插入 (預先知道要插入的資料列數) 透過在互斥鎖 (一種輕量級鎖定) 的控制下取得所需數量的自動遞增值來避免表級 AUTO-INC 鎖定,互斥鎖僅在配置過程期間持有,而不是直到語句完成。除非另一個交易持有 AUTO-INC 鎖,否則不會使用表級 AUTO-INC 鎖。如果另一個交易持有 AUTO-INC 鎖,則 簡單插入會等待 AUTO-INC 鎖,就像它是 大量插入一樣。

    此鎖定模式可確保,在存在預先不知道資料列數的 INSERT 語句 (以及在語句進行時分配自動遞增編號) 的情況下,任何INSERT 類似語句分配的所有自動遞增值都是連續的,並且操作對於基於語句的複製是安全的。

    簡單來說,這種鎖定模式顯著提高了可擴展性,同時對於基於語句的複製來說是安全的。此外,與傳統鎖定模式一樣,任何給定語句分配的自動遞增編號都是連續的。與傳統模式相比,任何使用自動遞增的語句的語義都沒有變化,但有一個重要的例外。

    例外情況是針對混合模式插入,在這種情況下,使用者為多列簡單插入中的某些列 (但不是所有列) 提供 AUTO_INCREMENT 欄位的明確值。對於此類插入,InnoDB 會分配比要插入的資料列數更多的自動遞增值。但是,所有自動分配的值都是連續產生的 (因此高於) 最近執行的前一個語句所產生的自動遞增值。過多的編號會遺失。

  • innodb_autoinc_lock_mode = 2 (交錯鎖定模式)

    在此鎖定模式下,沒有 INSERT 類型的語句會使用資料表層級的 AUTO-INC 鎖定,而且多個語句可以同時執行。這是最快且最可擴展的鎖定模式,但在從二進位日誌重播 SQL 語句時,使用基於語句的複製或復原情境下,此模式是不安全的

    在此鎖定模式下,可以保證自動遞增值在所有同時執行的 INSERT 類型的語句中是唯一且單調遞增的。但是,由於多個語句可以同時產生數字(也就是說,數字的分配在各個語句之間是交錯的),因此任何給定語句插入的列所產生值可能不是連續的。

    如果唯一執行的語句是 簡單插入,且要插入的列數是預先知道的,則單一語句產生的數字之間不會有間隙,除了 混合模式插入。但是,當執行 批量插入時,任何給定語句分配的自動遞增值中可能會存在間隙。

InnoDB AUTO_INCREMENT 鎖定模式的使用含義
  • 自動遞增與複製的使用

    如果您正在使用基於語句的複製,請將 innodb_autoinc_lock_mode 設定為 0 或 1,並在來源及其複本上使用相同的值。如果您使用 innodb_autoinc_lock_mode = 2(交錯)或來源和複本未使用相同鎖定模式的組態,則無法保證複本上的自動遞增值與來源上的相同。

    如果您正在使用基於列或混合格式的複製,則所有自動遞增鎖定模式都是安全的,因為基於列的複製對 SQL 語句的執行順序不敏感(而且混合格式會針對任何對基於語句的複製不安全的語句使用基於列的複製)。

  • 遺失的自動遞增值和序列間隙

    在所有鎖定模式(0、1 和 2)中,如果產生自動遞增值的交易回滾,則這些自動遞增值會被遺失。一旦為自動遞增欄位產生值,無論 INSERT 類型的語句是否完成,以及是否回滾包含的交易,都無法回滾。這些遺失的值不會被重複使用。因此,資料表的 AUTO_INCREMENT 欄位中儲存的值可能會存在間隙。

  • AUTO_INCREMENT 欄位指定 NULL 或 0

    在所有鎖定模式(0、1 和 2)中,如果使用者在 INSERT 中為 AUTO_INCREMENT 欄位指定 NULL 或 0,則 InnoDB 會將該列視為未指定值,並為其產生新值。

  • AUTO_INCREMENT 欄位指派負值

    在所有鎖定模式(0、1 和 2)中,如果您為 AUTO_INCREMENT 欄位指派負值,則自動遞增機制的行為是未定義的。

  • 如果 AUTO_INCREMENT 值大於指定整數類型的最大整數

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

  • 批量插入的自動遞增值間隙

    innodb_autoinc_lock_mode 設定為 0(傳統)或 1(連續)時,任何給定語句產生的自動遞增值都是連續的,沒有間隙,因為資料表層級的 AUTO-INC 鎖定會一直保留到語句結束,而且一次只能執行一個這樣的語句。

    innodb_autoinc_lock_mode 設定為 2(交錯)時,由 批量插入產生的自動遞增值中可能存在間隙,但只有在同時執行 INSERT 類型的語句時才會發生。

    對於鎖定模式 1 或 2,由於批量插入可能不知道每個語句所需的確切自動遞增值數量,而且可能存在高估,因此連續語句之間可能會出現間隙。

  • 混合模式插入指派的自動遞增值

    考慮一個 混合模式插入,其中一個 簡單插入為某些(但非全部)結果列指定了自動遞增值。此類語句在鎖定模式 0、1 和 2 中的行為不同。例如,假設 c1 是資料表 t1AUTO_INCREMENT 欄位,且最近自動產生的序號是 100。

    mysql> CREATE TABLE t1 (
        -> c1 INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, 
        -> c2 CHAR(1)
        -> ) ENGINE = INNODB;

    現在,考慮以下 混合模式插入語句

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

    innodb_autoinc_lock_mode 設定為 0(傳統)時,四個新列如下

    mysql> SELECT c1, c2 FROM t1 ORDER BY c2;
    +-----+------+
    | c1  | c2   |
    +-----+------+
    |   1 | a    |
    | 101 | b    |
    |   5 | c    |
    | 102 | d    |
    +-----+------+

    下一個可用的自動遞增值是 103,因為自動遞增值是一次分配一個,而不是在語句執行開始時一次分配所有。無論是否有同時執行的 INSERT 類型的語句(任何類型),此結果都成立。

    innodb_autoinc_lock_mode 設定為 1(連續)時,四個新列也是

    mysql> SELECT c1, c2 FROM t1 ORDER BY c2;
    +-----+------+
    | c1  | c2   |
    +-----+------+
    |   1 | a    |
    | 101 | b    |
    |   5 | c    |
    | 102 | d    |
    +-----+------+

    但是,在這種情況下,下一個可用的自動遞增值是 105,而不是 103,因為在處理語句時會分配四個自動遞增值,但只使用了兩個。無論是否有同時執行的 INSERT 類型的語句(任何類型),此結果都成立。

    innodb_autoinc_lock_mode 設定為 2(交錯)時,四個新列如下

    mysql> SELECT c1, c2 FROM t1 ORDER BY c2;
    +-----+------+
    | c1  | c2   |
    +-----+------+
    |   1 | a    |
    |   x | b    |
    |   5 | c    |
    |   y | d    |
    +-----+------+

    xy 的值是唯一的,且大於任何先前產生的列。但是,xy 的特定值取決於同時執行的語句所產生的自動遞增值數量。

    最後,考慮以下語句,當最近產生的序號為 100 時發出

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

    無論 innodb_autoinc_lock_mode 設定為何,此語句都會產生重複索引鍵錯誤 23000(無法寫入;資料表中存在重複索引鍵),因為 101 會分配給 (NULL, 'b') 列,而插入 (101, 'c') 列會失敗。

  • INSERT 語句序列的中間修改 AUTO_INCREMENT 欄位值

    如果您將 AUTO_INCREMENT 欄位值修改為大於目前最大自動遞增值的值,則會保留新值,後續的 INSERT 操作會從新的較大值開始分配自動遞增值。此行為會在以下範例中示範

    mysql> CREATE TABLE t1 (
        -> c1 INT NOT NULL AUTO_INCREMENT,
        -> PRIMARY KEY (c1)
        ->  ) ENGINE = InnoDB;
    
    mysql> INSERT INTO t1 VALUES(0), (0), (3);
    
    mysql> SELECT c1 FROM t1;
    +----+
    | c1 |
    +----+
    |  1 |
    |  2 |
    |  3 |
    +----+
    
    mysql> UPDATE t1 SET c1 = 4 WHERE c1 = 1;
    
    mysql> SELECT c1 FROM t1;
    +----+
    | c1 |
    +----+
    |  2 |
    |  3 |
    |  4 |
    +----+
    
    mysql> INSERT INTO t1 VALUES(0);
    
    mysql> SELECT c1 FROM t1;
    +----+
    | c1 |
    +----+
    |  2 |
    |  3 |
    |  4 |
    |  5 |
    +----+
InnoDB AUTO_INCREMENT 計數器初始化

本節說明 InnoDB 如何初始化 AUTO_INCREMENT 計數器。

如果您為 InnoDB 資料表指定 AUTO_INCREMENT 欄位,則記憶體中的資料表物件會包含一個稱為自動遞增計數器的特殊計數器,該計數器用於為欄位指派新值。

每次變更時,目前最大的自動遞增計數器值都會寫入重做日誌,並在每個檢查點儲存到資料字典;這使得目前最大的自動遞增計數器值在伺服器重新啟動後保持持久性。

在正常關機後重新啟動伺服器時,InnoDB 會使用儲存在資料字典中的目前最大自動遞增值來初始化記憶體中的自動遞增計數器。

在崩潰復原期間重新啟動伺服器時,InnoDB 會使用儲存在資料字典中的目前最大自動遞增值來初始化記憶體中的自動遞增計數器,並掃描重做日誌中自上次檢查點以來寫入的自動遞增計數器值。如果重做日誌的值大於記憶體中的計數器值,則會套用重做日誌的值。但是,如果伺服器意外結束,則無法保證重複使用先前分配的自動遞增值。每次因為 INSERTUPDATE 操作而變更目前的最大自動遞增值時,新值都會寫入重做日誌,但如果意外結束發生在將重做日誌刷新到磁碟之前,則在伺服器重新啟動後初始化自動遞增計數器時,可能會重複使用先前分配的值。

只有在InnoDB匯入沒有 .cfg 元數據檔案的表格時,才會使用相當於 SELECT MAX(ai_col) FROM table_name FOR UPDATE 語句來初始化自動遞增計數器。否則,如果存在 .cfg 元數據檔案,則會從該檔案讀取目前的自動遞增計數器最大值。除了計數器值的初始化之外,當嘗試使用 ALTER TABLE ... AUTO_INCREMENT = N 語句將計數器值設定為小於或等於持久化計數器值時,會使用相當於 SELECT MAX(ai_col) FROM table_name 語句來確定表格目前的自動遞增計數器最大值。例如,您可能會在刪除某些記錄後嘗試將計數器值設定為較小的值。在這種情況下,必須搜尋表格以確保新的計數器值不小於或等於實際的目前最大計數器值。

伺服器重新啟動不會取消 AUTO_INCREMENT = N 表格選項的效果。如果您將自動遞增計數器初始化為特定值,或者將自動遞增計數器值變更為較大的值,則新值會在伺服器重新啟動後持續存在。

注意

ALTER TABLE ... AUTO_INCREMENT = N 只能將自動遞增計數器值變更為大於目前最大值的值。

目前的最大自動遞增值會被持久化,以防止重複使用先前配置的值。

如果 SHOW TABLE STATUS 語句在自動遞增計數器初始化之前檢查表格,則 InnoDB 會開啟表格,並使用儲存在資料字典中的目前最大自動遞增值來初始化計數器值。然後,該值會儲存在記憶體中,以供稍後的插入或更新使用。計數器值的初始化會在表格上使用正常的互斥鎖定讀取,該讀取會持續到交易結束。InnoDB 在為使用者指定的大於 0 的自動遞增值的新建立表格初始化自動遞增計數器時,會遵循相同的程序。

在初始化自動遞增計數器之後,如果您在插入資料列時未明確指定自動遞增值,則 InnoDB 會隱式遞增計數器,並將新值指派給欄。如果您插入的資料列明確指定了自動遞增欄值,且該值大於目前的計數器最大值,則計數器會設定為指定的值。

InnoDB 會在伺服器執行時使用記憶體中的自動遞增計數器。當伺服器停止並重新啟動時,InnoDB 會依照先前所述重新初始化自動遞增計數器。

auto_increment_offset 變數決定了 AUTO_INCREMENT 欄值的起始點。預設設定為 1。

auto_increment_increment 變數控制連續欄值之間的間隔。預設設定為 1。

注意

AUTO_INCREMENT 整數欄用完值時,後續的 INSERT 操作會傳回重複鍵錯誤。這是 MySQL 的一般行為。