mysql8 翻譯系列 七十三

17.6.1.4 移動(dòng)或復(fù)制InnoDB表

本節(jié)介紹將部分或全部InnoDB表移動(dòng)或復(fù)制到不同服務(wù)器或?qū)嵗姆椒ā@纾憧赡苄枰獙⒄麄€(gè)MySQL實(shí)例遷移到更大、更快的服務(wù)器上;可能需要將整個(gè)MySQL實(shí)例克隆到新的副本服務(wù)器上;也可能需要將單個(gè)表復(fù)制到另一個(gè)實(shí)例上,用于開發(fā)和測(cè)試應(yīng)用程序,或者復(fù)制到數(shù)據(jù)倉庫服務(wù)器上生成報(bào)表。

在Windows系統(tǒng)上,InnoDB始終在內(nèi)部以小寫形式存儲(chǔ)數(shù)據(jù)庫和表名。若要以二進(jìn)制格式在Unix和Windows之間移動(dòng)數(shù)據(jù)庫,創(chuàng)建所有數(shù)據(jù)庫和表時(shí)都應(yīng)使用小寫名稱。在創(chuàng)建任何數(shù)據(jù)庫或表之前,在my.cnfmy.ini文件的[mysqld]部分添加以下行,可方便地實(shí)現(xiàn)這一點(diǎn):

[mysqld]
lower_case_table_names=1

注意:禁止使用與服務(wù)器初始化時(shí)不同的lower_case_table_names設(shè)置啟動(dòng)服務(wù)器。

移動(dòng)或復(fù)制InnoDB表的方法包括:

  • 導(dǎo)入表
  • MySQL企業(yè)備份
  • 復(fù)制數(shù)據(jù)文件(冷備份方法)
  • 從邏輯備份恢復(fù)

導(dǎo)入表

位于獨(dú)立表空間中的表,可以使用可傳輸表空間功能從另一個(gè)MySQL服務(wù)器實(shí)例或備份中導(dǎo)入。詳見17.6.1.3節(jié) “導(dǎo)入InnoDB表”。

MySQL企業(yè)備份

MySQL企業(yè)備份產(chǎn)品允許你在對(duì)操作造成最小干擾的情況下備份運(yùn)行中的MySQL數(shù)據(jù)庫,同時(shí)生成數(shù)據(jù)庫的一致性快照。在MySQL企業(yè)備份復(fù)制表時(shí),讀寫操作可以繼續(xù)進(jìn)行。此外,MySQL企業(yè)備份可以創(chuàng)建壓縮備份文件,并備份部分表。結(jié)合MySQL二進(jìn)制日志,還可以執(zhí)行時(shí)間點(diǎn)恢復(fù)。MySQL企業(yè)備份是MySQL企業(yè)訂閱服務(wù)的一部分。

有關(guān)MySQL企業(yè)備份的更多詳細(xì)信息,參見32.1節(jié) “MySQL企業(yè)備份概述”。

復(fù)制數(shù)據(jù)文件(冷備份方法)

通過復(fù)制17.18.1節(jié) “InnoDB備份” 中 “冷備份” 下列出的所有相關(guān)文件,就可以簡(jiǎn)單地移動(dòng)InnoDB數(shù)據(jù)庫。

InnoDB數(shù)據(jù)文件和日志文件在所有具有相同浮點(diǎn)數(shù)格式的平臺(tái)上都是二進(jìn)制兼容的。如果浮點(diǎn)數(shù)格式不同,但表中未使用FLOATDOUBLE數(shù)據(jù)類型,那么操作過程是一樣的:只需復(fù)制相關(guān)文件即可。

在移動(dòng)或復(fù)制獨(dú)立表空間的.ibd文件時(shí),源系統(tǒng)和目標(biāo)系統(tǒng)上的數(shù)據(jù)庫目錄名必須相同。存儲(chǔ)在InnoDB共享表空間中的表定義包含數(shù)據(jù)庫名,并且表空間文件中存儲(chǔ)的事務(wù)ID和日志序列號(hào)在不同數(shù)據(jù)庫之間也有所不同。

要將.ibd文件及其關(guān)聯(lián)的表從一個(gè)數(shù)據(jù)庫移動(dòng)到另一個(gè)數(shù)據(jù)庫,可以使用RENAME TABLE語句:

RENAME TABLE db1.tbl_name TO db2.tbl_name;

如果你有一個(gè) “干凈的”.ibd文件備份,可以按以下步驟將其恢復(fù)到原始的MySQL安裝環(huán)境中:

  • 自復(fù)制.ibd文件以來,該表不能被刪除或截?cái)啵驗(yàn)檫@樣會(huì)更改表空間內(nèi)部存儲(chǔ)的表ID。
  • 執(zhí)行以下ALTER TABLE語句刪除當(dāng)前的.ibd文件:
ALTER TABLE tbl_name DISCARD TABLESPACE;
  • 將備份的.ibd文件復(fù)制到相應(yīng)的數(shù)據(jù)庫目錄。
  • 執(zhí)行以下ALTER TABLE語句,告知InnoDB使用新的.ibd文件作為該表的存儲(chǔ)文件:
ALTER TABLE tbl_name IMPORT TABLESPACE;

注意ALTER TABLE ... IMPORT TABLESPACE功能不會(huì)對(duì)導(dǎo)入的數(shù)據(jù)強(qiáng)制執(zhí)行外鍵約束。

在此情況下,“干凈的”.ibd文件備份需滿足以下條件:

  • .ibd文件中不存在未提交的事務(wù)修改。
  • .ibd文件中不存在未合并的插入緩沖條目。
  • 清除操作已從.ibd文件中刪除所有標(biāo)記為刪除的索引記錄。
  • mysqld已將.ibd文件的所有修改頁從緩沖池刷新到文件中。

可以使用以下方法創(chuàng)建一個(gè)干凈的.ibd文件備份:

  • 停止mysqld服務(wù)器的所有活動(dòng),并提交所有事務(wù)。
  • 等待SHOW ENGINE INNODB STATUS顯示數(shù)據(jù)庫中沒有活動(dòng)事務(wù),并且InnoDB的主線程狀態(tài)為Waiting for server activity,然后就可以復(fù)制.ibd文件。

另一種創(chuàng)建干凈的.ibd文件副本的方法是使用MySQL企業(yè)備份產(chǎn)品:

  • 使用MySQL企業(yè)備份對(duì)InnoDB安裝進(jìn)行備份。
  • 在備份上啟動(dòng)第二個(gè)mysqld服務(wù)器,讓它清理備份中的.ibd文件。

從邏輯備份恢復(fù)

可以使用mysqldump等工具執(zhí)行邏輯備份,該備份會(huì)生成一組SQL語句,執(zhí)行這些語句可以重現(xiàn)原始數(shù)據(jù)庫對(duì)象定義和表數(shù)據(jù),以便傳輸?shù)搅硪粋€(gè)SQL服務(wù)器。使用此方法時(shí),格式差異或表中是否包含浮點(diǎn)數(shù)據(jù)都不會(huì)產(chǎn)生影響。

為提高此方法的性能,在導(dǎo)入數(shù)據(jù)時(shí)應(yīng)禁用autocommit,僅在導(dǎo)入整個(gè)表或表的一部分后執(zhí)行提交操作。

17.6.1.6 InnoDB中的AUTO_INCREMENT處理

InnoDB提供了一種可配置的鎖定機(jī)制,對(duì)于向包含AUTO_INCREMENT列的表中添加行的SQL語句而言,該機(jī)制能顯著提升其可擴(kuò)展性和性能。要在InnoDB表中使用AUTO_INCREMENT機(jī)制,AUTO_INCREMENT列必須定義為某個(gè)索引的首列或唯一列,這樣才能通過對(duì)表執(zhí)行類似SELECT MAX(<ai_col>)的索引查找操作,獲取該列的最大值。該索引不必是PRIMARY KEYUNIQUE索引,但為避免AUTO_INCREMENT列出現(xiàn)重復(fù)值,建議使用這些索引類型。

本節(jié)將介紹AUTO_INCREMENT的鎖定模式、不同AUTO_INCREMENT鎖定模式設(shè)置的使用影響,以及InnoDB如何初始化AUTO_INCREMENT計(jì)數(shù)器。

  • InnoDB AUTO_INCREMENT鎖定模式
  • InnoDB AUTO_INCREMENT鎖定模式的使用影響
  • InnoDB AUTO_INCREMENT計(jì)數(shù)器初始化
  • 注意事項(xiàng)

InnoDB AUTO_INCREMENT鎖定模式

本節(jié)描述用于生成自增長值的AUTO_INCREMENT鎖定模式,以及每種鎖定模式對(duì)復(fù)制的影響。自增長鎖定模式在啟動(dòng)時(shí)通過innodb_autoinc_lock_mode變量進(jìn)行配置。

在描述innodb_autoinc_lock_mode設(shè)置時(shí),會(huì)用到以下術(shù)語:

  • INSERT類” 語句:所有在表中生成新行的語句,包括INSERTINSERT ... SELECTREPLACEREPLACE ... SELECTLOAD DATA。涵蓋 “簡(jiǎn)單插入”、“批量插入” 和 “混合模式” 插入。
  • “簡(jiǎn)單插入”:在語句初始處理時(shí),就能確定要插入行數(shù)的語句。這包括不包含嵌套子查詢的單行和多行INSERTREPLACE語句,但不包括INSERT ... ON DUPLICATE KEY UPDATE
  • “批量插入”:無法預(yù)先確定要插入的行數(shù)(以及所需自增長值的數(shù)量)的語句。這包括INSERT ... SELECTREPLACE ... SELECTLOAD DATA語句,但不包括普通的INSERT語句。InnoDB在處理每一行時(shí),會(huì)為AUTO_INCREMENT列逐個(gè)分配新值。
  • “混合模式插入”:這是一種 “簡(jiǎn)單插入” 語句,為部分(而非全部)新行指定自增長值。例如,假設(shè)c1是表t1AUTO_INCREMENT列:
INSERT INTO t1 (c1,c2) VALUES (1,'a'), (NULL,'b'), (5,'c'), (NULL,'d');

另一種 “混合模式插入” 是INSERT ... ON DUPLICATE KEY UPDATE,在最壞的情況下,它實(shí)際上是先執(zhí)行INSERT,再執(zhí)行UPDATE,在更新階段,為AUTO_INCREMENT列分配的值可能會(huì)使用,也可能不會(huì)使用 。

innodb_autoinc_lock_mode變量有三種可能的設(shè)置:0、1或2,分別對(duì)應(yīng) “傳統(tǒng)”、“連續(xù)” 或 “交錯(cuò)” 鎖定模式。從MySQL 8.0開始,交錯(cuò)鎖定模式(innodb_autoinc_lock_mode=2)是默認(rèn)設(shè)置。在MySQL 8.0之前,連續(xù)鎖定模式是默認(rèn)設(shè)置(innodb_autoinc_lock_mode=1)。

MySQL 8.0中默認(rèn)的交錯(cuò)鎖定模式,反映了從基于語句的復(fù)制到基于行的復(fù)制作為默認(rèn)復(fù)制類型的轉(zhuǎn)變。基于語句的復(fù)制要求連續(xù)的自增長鎖定模式,以確保對(duì)于給定的SQL語句序列,自增長值能以可預(yù)測(cè)且可重復(fù)的順序分配,而基于行的復(fù)制對(duì)SQL語句的執(zhí)行順序不敏感。

  • innodb_autoinc_lock_mode = 0(“傳統(tǒng)” 鎖定模式):傳統(tǒng)鎖定模式的行為與innodb_autoinc_lock_mode變量引入之前相同。提供此傳統(tǒng)鎖定模式選項(xiàng)是為了實(shí)現(xiàn)向后兼容、性能測(cè)試,以及解決 “混合模式插入” 因語義差異可能產(chǎn)生的問題。

在這種鎖定模式下,所有 “INSERT類” 語句在向包含AUTO_INCREMENT列的表中插入數(shù)據(jù)時(shí),都會(huì)獲取一個(gè)特殊的表級(jí)AUTO-INC鎖。該鎖通常會(huì)持有到語句結(jié)束(而非事務(wù)結(jié)束),以確保對(duì)于給定的INSERT語句序列,自增長值能以可預(yù)測(cè)且可重復(fù)的順序分配,并確保任何給定語句分配的自增長值是連續(xù)的。

在基于語句的復(fù)制場(chǎng)景中,這意味著當(dāng)一條SQL語句在副本服務(wù)器上復(fù)制時(shí),自增長列使用的值與源服務(wù)器上的相同。多個(gè)INSERT語句的執(zhí)行結(jié)果是確定的,副本會(huì)再現(xiàn)與源服務(wù)器相同的數(shù)據(jù)。如果多個(gè)INSERT語句生成的自增長值交錯(cuò),那么兩個(gè)并發(fā)INSERT語句的結(jié)果將是不確定的,并且無法使用基于語句的復(fù)制可靠地傳播到副本服務(wù)器。

為了更清楚地說明,考慮使用以下表的示例:

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

假設(shè)有兩個(gè)事務(wù)正在運(yùn)行,每個(gè)事務(wù)都向包含AUTO_INCREMENT列的表中插入行。一個(gè)事務(wù)使用INSERT ... SELECT語句插入1000行,另一個(gè)事務(wù)使用簡(jiǎn)單的INSERT語句插入一行:

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

InnoDB無法預(yù)先知道Tx1的INSERT語句中從SELECT檢索到多少行,它會(huì)在語句執(zhí)行過程中逐個(gè)分配自增長值。由于持有表級(jí)鎖直到語句結(jié)束,一次只能執(zhí)行一個(gè)引用表t1INSERT語句,不同語句生成的自增長值不會(huì)交錯(cuò)。Tx1的INSERT ... SELECT語句生成的自增長值是連續(xù)的,Tx2的INSERT語句使用的(單個(gè))自增長值,根據(jù)哪個(gè)語句先執(zhí)行,要么小于要么大于Tx1使用的所有自增長值。

只要從二進(jìn)制日志重放SQL語句時(shí)(在使用基于語句的復(fù)制或恢復(fù)場(chǎng)景中)執(zhí)行順序相同,結(jié)果就與Tx1和Tx2首次運(yùn)行時(shí)相同。因此,持有表級(jí)鎖直到語句結(jié)束,使得使用自增長的INSERT語句在基于語句的復(fù)制中使用是安全的。然而,當(dāng)多個(gè)事務(wù)同時(shí)執(zhí)行插入語句時(shí),這些表級(jí)鎖會(huì)限制并發(fā)性和可擴(kuò)展性。

在前面的示例中,如果沒有表級(jí)鎖,Tx2中INSERT使用的自增長列的值將精確取決于該語句的執(zhí)行時(shí)間。如果Tx2的INSERT在Tx1的INSERT運(yùn)行時(shí)執(zhí)行(而不是在其開始之前或完成之后),兩個(gè)INSERT語句分配的特定自增長值將是不確定的,并且每次運(yùn)行可能會(huì)有所不同。

在連續(xù)鎖定模式下,InnoDB可以避免對(duì)預(yù)先知道插入行數(shù)的 “簡(jiǎn)單插入” 語句使用表級(jí)AUTO-INC鎖,同時(shí)仍然保持基于語句的復(fù)制的確定性執(zhí)行和安全性。

如果不使用二進(jìn)制日志作為恢復(fù)或復(fù)制的一部分來重放SQL語句,交錯(cuò)鎖定模式可用于完全消除表級(jí)AUTO-INC鎖的使用,以實(shí)現(xiàn)更高的并發(fā)性和性能,代價(jià)是允許語句分配的自增長值中出現(xiàn)間隙,并且可能會(huì)使并發(fā)執(zhí)行的語句分配的自增長值交錯(cuò)。

  • innodb_autoinc_lock_mode = 1(“連續(xù)” 鎖定模式):在這種模式下,“批量插入” 使用特殊的表級(jí)AUTO-INC鎖,并持有該鎖直到語句結(jié)束。這適用于所有INSERT ... SELECTREPLACE ... SELECTLOAD DATA語句。一次只能執(zhí)行一個(gè)持有AUTO-INC鎖的語句。如果批量插入操作的源表與目標(biāo)表不同,在從源表選擇的第一行上獲取共享鎖后,會(huì)獲取目標(biāo)表上的AUTO-INC鎖。如果批量插入操作的源表和目標(biāo)表相同,則在對(duì)所有選定行獲取共享鎖后,獲取AUTO-INC鎖。

“簡(jiǎn)單插入”(預(yù)先知道要插入的行數(shù))通過在互斥鎖(一種輕量級(jí)鎖)的控制下獲取所需數(shù)量的自增長值,避免使用表級(jí)AUTO-INC鎖。該互斥鎖僅在分配過程中持有,而不是直到語句完成。除非另一個(gè)事務(wù)持有AUTO-INC鎖,否則不會(huì)使用表級(jí)AUTO-INC鎖。如果另一個(gè)事務(wù)持有AUTO-INC鎖,“簡(jiǎn)單插入” 會(huì)像 “批量插入” 一樣等待AUTO-INC鎖。

這種鎖定模式確保在存在無法預(yù)先知道插入行數(shù)(且自增長值在語句執(zhí)行過程中分配)的INSERT語句時(shí),任何 “INSERT類” 語句分配的所有自增長值都是連續(xù)的,并且對(duì)于基于語句的復(fù)制,操作是安全的。

簡(jiǎn)單來說,這種鎖定模式在確保基于語句的復(fù)制安全使用的同時(shí),顯著提高了可擴(kuò)展性。此外,與 “傳統(tǒng)” 鎖定模式一樣,任何給定語句分配的自增長值都是連續(xù)的。與 “傳統(tǒng)” 模式相比,除了一個(gè)重要的例外情況,使用自增長的任何語句的語義都沒有變化。

這個(gè)例外情況是 “混合模式插入”,在多行 “簡(jiǎn)單插入” 中,用戶為部分(而非全部)行顯式提供AUTO_INCREMENT列的值。對(duì)于這樣的插入,InnoDB分配的自增長值數(shù)量會(huì)多于要插入的行數(shù)。然而,所有自動(dòng)分配的值都是連續(xù)生成的(因此大于)最近執(zhí)行的上一條語句生成的自增長值。“多余” 的值會(huì)被丟棄。

  • innodb_autoinc_lock_mode = 2(“交錯(cuò)” 鎖定模式):在這種鎖定模式下,沒有 “INSERT類” 語句使用表級(jí)AUTO-INC鎖,多個(gè)語句可以同時(shí)執(zhí)行。這是最快且可擴(kuò)展性最強(qiáng)的鎖定模式,但在使用基于語句的復(fù)制或從二進(jìn)制日志重放SQL語句的恢復(fù)場(chǎng)景中,它并不安全。

在這種鎖定模式下,保證所有并發(fā)執(zhí)行的 “INSERT類” 語句生成的自增長值是唯一且單調(diào)遞增的。然而,由于多個(gè)語句可以同時(shí)生成值(即值的分配在不同語句間交錯(cuò)),任何給定語句為插入行生成的值可能不連續(xù)。

如果執(zhí)行的唯一語句是預(yù)先知道插入行數(shù)的 “簡(jiǎn)單插入”,除了 “混合模式插入” 之外,單個(gè)語句生成的值不會(huì)有間隙。但是,當(dāng)執(zhí)行 “批量插入” 時(shí),任何給定語句分配的自增長值可能會(huì)有間隙。

InnoDB AUTO_INCREMENT鎖定模式的使用影響

  • 使用自增長與復(fù)制:如果使用基于語句的復(fù)制,將innodb_autoinc_lock_mode設(shè)置為0或1,并在源服務(wù)器及其副本上使用相同的值。如果使用innodb_autoinc_lock_mode = 2(“交錯(cuò)”),或者源服務(wù)器和副本使用不同的鎖定模式配置,無法確保副本上的自增長值與源服務(wù)器上的相同。

如果使用基于行的復(fù)制或混合格式復(fù)制,所有自增長鎖定模式都是安全的,因?yàn)榛谛械膹?fù)制對(duì)SQL語句的執(zhí)行順序不敏感(并且混合格式對(duì)任何基于語句的復(fù)制不安全的語句使用基于行的復(fù)制)。

  • “丟失” 的自增長值和序列間隙:在所有鎖定模式(0、1和2)下,如果生成自增長值的事務(wù)回滾,這些自增長值就會(huì) “丟失”。一旦為自增長列生成了一個(gè)值,無論 “INSERT類” 語句是否完成,也無論包含該語句的事務(wù)是否回滾,該值都不能回滾。這些丟失的值不會(huì)被重用。因此,表的AUTO_INCREMENT列中存儲(chǔ)的值可能會(huì)有間隙。
  • 為AUTO_INCREMENT列指定NULL或0:在所有鎖定模式(0、1和2)下,如果用戶在INSERT語句中為AUTO_INCREMENT列指定NULL或0,InnoDB會(huì)將該行視為未指定該值,并為其生成一個(gè)新值。
  • 為AUTO_INCREMENT列分配負(fù)值:在所有鎖定模式(0、1和2)下,如果為AUTO_INCREMENT列分配負(fù)值,自增長機(jī)制的行為是未定義的。
  • 如果AUTO_INCREMENT值大于指定整數(shù)類型的最大值:在所有鎖定模式(0、1和2)下,如果自增長值大于指定整數(shù)類型所能存儲(chǔ)的最大整數(shù),自增長機(jī)制的行為是未定義的。
  • “批量插入” 的自增長值間隙:將innodb_autoinc_lock_mode設(shè)置為0(“傳統(tǒng)”)或1(“連續(xù)”)時(shí),任何給定語句生成的自增長值是連續(xù)的,沒有間隙,因?yàn)楸砑?jí)AUTO-INC鎖會(huì)持有到語句結(jié)束,并且一次只能執(zhí)行一個(gè)這樣的語句。

innodb_autoinc_lock_mode設(shè)置為2(“交錯(cuò)”)時(shí),“批量插入” 生成的自增長值可能會(huì)有間隙,但前提是存在并發(fā)執(zhí)行的 “INSERT類” 語句。

對(duì)于鎖定模式1或2,連續(xù)語句之間可能會(huì)出現(xiàn)間隙,因?yàn)閷?duì)于批量插入,可能無法確切知道每個(gè)語句所需的自增長值數(shù)量,并且可能會(huì)高估。

  • “混合模式插入” 分配的自增長值:考慮 “混合模式插入”,即 “簡(jiǎn)單插入” 為部分(而非全部)結(jié)果行指定自增長值。這樣的語句在鎖定模式0、1和2下的行為有所不同。例如,假設(shè)c1是表t1AUTO_INCREMENT列,并且最近自動(dòng)生成的序列號(hào)是100。
mysql> CREATE TABLE t1 (
    -> c1 INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, 
    -> c2 CHAR(1)
    -> ) ENGINE = INNODB;

現(xiàn)在,考慮以下 “混合模式插入” 語句:

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

當(dāng)innodb_autoinc_lock_mode設(shè)置為0(“傳統(tǒng)”)時(shí),四條新行如下:

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

下一個(gè)可用的自增長值是103,因?yàn)樽栽鲩L值是逐個(gè)分配的,而不是在語句執(zhí)行開始時(shí)一次性分配所有值。無論是否有并發(fā)執(zhí)行的 “INSERT類” 語句(任何類型),結(jié)果都是如此。

當(dāng)innodb_autoinc_lock_mode設(shè)置為1(“連續(xù)”)時(shí),四條新行也是:

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

然而,在這種情況下,下一個(gè)可用的自增長值是105,而不是103,因?yàn)樵谔幚碚Z句時(shí)分配了四個(gè)自增長值,但只使用了兩個(gè)。無論是否有并發(fā)執(zhí)行的 “INSERT類” 語句(任何類型),結(jié)果都是如此。

當(dāng)innodb_autoinc_lock_mode設(shè)置為2(“交錯(cuò)”)時(shí),四條新行如下:

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

xy的值是唯一的,并且大于之前生成的任何行的值。然而,xy的具體值取決于并發(fā)執(zhí)行語句生成的自增長值數(shù)量。

最后,考慮以下語句,在最近生成的序列號(hào)為100時(shí)執(zhí)行:

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

無論innodb_autoinc_lock_mode如何設(shè)置,此語句都會(huì)生成一個(gè)重復(fù)鍵錯(cuò)誤23000(Can't write; duplicate key in table),因?yàn)闉?code>(NULL, 'b')行分配了101,而插入(101, 'c')行時(shí)失敗。

  • 在INSERT語句序列中間修改AUTO_INCREMENT列的值:在MySQL 5.7及更早版本中,在INSERT語句序列中間修改AUTO_INCREMENT列的值可能會(huì)導(dǎo)致 “重復(fù)條目” 錯(cuò)誤。例如,如果執(zhí)行一個(gè)UPDATE操作,將AUTO_INCREMENT列的值更改為大于當(dāng)前最大自增長值,后續(xù)未指定未使用自增長值的INSERT操作可能會(huì)遇到 “重復(fù)條目” 錯(cuò)誤。在MySQL 8.0及更高版本中,如果將AUTO_INCREMENT列的值修改為大于當(dāng)前最大自增長值,新值將被持久化,后續(xù)的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計(jì)數(shù)器初始化

本節(jié)介紹InnoDB如何初始化AUTO_INCREMENT計(jì)數(shù)器。

如果為InnoDB表指定了AUTO_INCREMENT列,內(nèi)存中的表對(duì)象會(huì)包含一個(gè)特殊的計(jì)數(shù)器,即自增長計(jì)數(shù)器,用于為該列分配新值。

在MySQL 5.7及更早版本中,自增長計(jì)數(shù)器存儲(chǔ)在內(nèi)存中,而非磁盤上。服務(wù)器重啟后,為初始化自增長計(jì)數(shù)器,InnoDB會(huì)在首次向包含AUTO_INCREMENT列的表中插入數(shù)據(jù)時(shí),執(zhí)行類似如下的語句:

SELECT MAX(ai_col) FROM table_name FOR UPDATE;

在MySQL 8.0中,這種行為有所改變。每次當(dāng)前最大自增長計(jì)數(shù)器的值發(fā)生變化時(shí),都會(huì)將其寫入重做日志,并在每次檢查點(diǎn)時(shí)保存到數(shù)據(jù)字典中。這些更改使得當(dāng)前最大自增長計(jì)數(shù)器的值在服務(wù)器重啟后仍然有效。

在正常關(guān)閉后重啟服務(wù)器時(shí),InnoDB會(huì)使用數(shù)據(jù)字典中存儲(chǔ)的當(dāng)前最大自增長值,初始化內(nèi)存中的自增長計(jì)數(shù)器。

在崩潰恢復(fù)期間重啟服務(wù)器時(shí),InnoDB會(huì)使用數(shù)據(jù)字典中存儲(chǔ)的當(dāng)前最大自增長值初始化內(nèi)存中的自增長計(jì)數(shù)器,并掃描重做日志,查找自上次檢查點(diǎn)以來寫入的自增長計(jì)數(shù)器值。如果重做日志中記錄的值大于內(nèi)存中的計(jì)數(shù)器值,則應(yīng)用該值。但是,在服務(wù)器意外退出的情況下,無法保證不會(huì)重用之前分配的自增長值。每次由于INSERTUPDATE操作導(dǎo)致當(dāng)前最大自增長值發(fā)生變化時(shí),新值都會(huì)寫入重做日志,但如果在重做日志刷新到磁盤之前服務(wù)器意外退出,那么在服務(wù)器重啟后初始化自增長計(jì)數(shù)器時(shí),之前分配的值可能會(huì)被重用。

只有在導(dǎo)入表且沒有.cfg元數(shù)據(jù)文件的情況下,InnoDB才會(huì)使用類似SELECT MAX(ai_col) FROM <table_name> FOR UPDATE的語句來初始化自增長計(jì)數(shù)器。否則,如果存在.cfg元數(shù)據(jù)文件,會(huì)從該文件中讀取當(dāng)前最大自增長計(jì)數(shù)器的值。除了計(jì)數(shù)器值初始化之外,當(dāng)嘗試使用ALTER TABLE ... AUTO_INCREMENT = <N>語句將計(jì)數(shù)器值設(shè)置為小于或等于持久化的計(jì)數(shù)器值時(shí),會(huì)使用類似SELECT MAX(ai_col) FROM <table_name>的語句來確定表的當(dāng)前最大自增長計(jì)數(shù)器值。例如,在刪除某些記錄后,可能會(huì)嘗試將計(jì)數(shù)器值設(shè)置為較小的值。在這種情況下,必須檢查表以確保新的計(jì)數(shù)器值不小于或等于實(shí)際的當(dāng)前最大計(jì)數(shù)器值。

在MySQL 5.7及更早版本中,服務(wù)器重啟會(huì)取消AUTO_INCREMENT = N表選項(xiàng)的效果,該選項(xiàng)可在CREATE TABLEALTER TABLE語句中分別用于設(shè)置初始計(jì)數(shù)器值或更改現(xiàn)有計(jì)數(shù)器值。在MySQL 8.0中,服務(wù)器重啟不會(huì)取消AUTO_INCREMENT = N表選項(xiàng)的效果。如果將自增長計(jì)數(shù)器初始化為特定值,或者將自增長計(jì)數(shù)器值更改為更大的值,新值會(huì)在服務(wù)器重啟后仍然有效。
注意ALTER TABLE ... AUTO_INCREMENT = N只能將自增長計(jì)數(shù)器值更改為大于當(dāng)前最大值的值。

在MySQL 5.7及更早版本中,在ROLLBACK操作后立即重啟服務(wù)器,可能會(huì)導(dǎo)致重用之前分配給回滾事務(wù)的自增長值,實(shí)際上是回滾了當(dāng)前最大自增長值。在MySQL 8.0中,當(dāng)前最大自增長值是持久化的,可防止重用之前分配的值。

如果在自增長計(jì)數(shù)器初始化之前,SHOW TABLE STATUS語句檢查一個(gè)表,InnoDB會(huì)打開該表,并使用存儲(chǔ)在數(shù)據(jù)字典中的當(dāng)前最大自增長值初始化計(jì)數(shù)器值。然后,該值會(huì)存儲(chǔ)在內(nèi)存中,供后續(xù)的插入或更新操作使用。計(jì)數(shù)器值的初始化使用對(duì)表的普通排他鎖定讀取,該讀取操作會(huì)持續(xù)到事務(wù)結(jié)束。對(duì)于新創(chuàng)建的、用戶指定自增長值大于0的表,InnoDB在初始化自增長計(jì)數(shù)器時(shí),也會(huì)遵循相同的過程。

自增長計(jì)數(shù)器初始化后,如果在插入行時(shí)未顯式指定自增長值,InnoDB會(huì)隱式遞增計(jì)數(shù)器,并將新值分配給該列。如果插入的行顯式指定了自增長列的值,且該值大于當(dāng)前最大計(jì)數(shù)器值,則會(huì)將計(jì)數(shù)器設(shè)置為指定的值。

只要服務(wù)器在運(yùn)行,InnoDB就會(huì)使用內(nèi)存中的自增長計(jì)數(shù)器。服務(wù)器停止并重啟時(shí),InnoDB會(huì)如前所述重新初始化自增長計(jì)數(shù)器。

auto_increment_offset變量確定AUTO_INCREMENT列值的起始點(diǎn),默認(rèn)設(shè)置為1。

auto_increment_increment變量控制連續(xù)列值之間的間隔,默認(rèn)設(shè)置為1。

注意事項(xiàng)

當(dāng)AUTO_INCREMENT整數(shù)列的值用完時(shí),后續(xù)的INSERT操作會(huì)返回重復(fù)鍵錯(cuò)誤。這是MySQL的常規(guī)行為。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,412評(píng)論 6 532
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,514評(píng)論 3 416
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,373評(píng)論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,975評(píng)論 1 312
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,743評(píng)論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,199評(píng)論 1 324
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,262評(píng)論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,414評(píng)論 0 288
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,951評(píng)論 1 336
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,780評(píng)論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,983評(píng)論 1 369
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,527評(píng)論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,218評(píng)論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,649評(píng)論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,889評(píng)論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,673評(píng)論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,967評(píng)論 2 374

推薦閱讀更多精彩內(nèi)容