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


MySQL 9.0 參考手冊  /  ...  /  InnoDB 中不同 SQL 語句設定的鎖定

17.7.3 InnoDB 中不同 SQL 語句設定的鎖定

鎖定讀取UPDATEDELETE 通常會在 SQL 語句處理過程中掃描的每個索引記錄上設定記錄鎖定。語句中是否有會排除該列的 WHERE 條件並不重要。InnoDB 不會記住確切的 WHERE 條件,而只知道掃描了哪些索引範圍。鎖定通常是next-key 鎖定,也會阻止在記錄正前方的 間隙中插入資料。但是,可以明確停用間隙鎖定,這會導致不使用 next-key 鎖定。如需詳細資訊,請參閱第 17.7.1 節「InnoDB 鎖定」。交易隔離等級也會影響設定的鎖定;請參閱第 17.7.2.1 節「交易隔離等級」

如果在搜尋中使用次要索引,且要設定的索引記錄鎖定是獨佔的,InnoDB 也會擷取對應的叢集索引記錄,並在這些記錄上設定鎖定。

如果您的語句沒有合適的索引,且 MySQL 必須掃描整個資料表才能處理該語句,則資料表的每一列都會被鎖定,進而阻止其他使用者向資料表插入任何資料。建立良好的索引非常重要,這樣您的查詢就不會掃描超出必要的列。

InnoDB 設定特定類型的鎖定,如下所示。

  • SELECT ... FROM 是一個一致性讀取,讀取資料庫的快照,除非將交易隔離層級設定為 SERIALIZABLE,否則不會設定任何鎖定。對於 SERIALIZABLE 層級,搜尋會在遇到的索引記錄上設定共用鍵鎖定 (shared next-key locks)。然而,對於使用唯一索引搜尋唯一列的鎖定列的語句,只需要索引記錄鎖定。

  • 使用唯一索引的 SELECT ... FOR UPDATESELECT ... FOR SHARE 語句,會為掃描的列取得鎖定,並釋放不符合結果集條件的列的鎖定(例如,如果它們不符合 WHERE 子句中給定的條件)。然而,在某些情況下,列可能不會立即解除鎖定,因為在查詢執行期間,結果列與其原始來源之間的關係會遺失。例如,在 UNION 中,來自資料表的掃描(和鎖定)列可能會在評估它們是否符合結果集條件之前插入到暫存資料表。在這種情況下,暫存資料表中的列與原始資料表中的列的關係會遺失,並且後者列直到查詢執行結束才會解除鎖定。

  • 對於鎖定讀取(使用 FOR UPDATEFOR SHARESELECT)、UPDATEDELETE 語句,取得的鎖定取決於該語句是使用帶有唯一搜尋條件的唯一索引,還是範圍型搜尋條件。

    • 對於帶有唯一搜尋條件的唯一索引,InnoDB 僅鎖定找到的索引記錄,而不是它之前的間隙

    • 對於其他搜尋條件,以及非唯一索引,InnoDB 會鎖定掃描的索引範圍,使用間隙鎖定鍵鎖定來阻止其他工作階段將資料插入到該範圍所涵蓋的間隙中。有關間隙鎖定和鍵鎖定的資訊,請參閱第 17.7.1 節「InnoDB 鎖定」

  • 對於搜尋遇到的索引記錄,SELECT ... FOR UPDATE 會阻止其他工作階段執行 SELECT ... FOR SHARE 或在某些交易隔離層級中讀取。一致性讀取會忽略讀取視圖中存在的記錄上設定的任何鎖定。

  • UPDATE ... WHERE ... 會在搜尋遇到的每個記錄上設定獨佔鍵鎖定。然而,對於使用唯一索引搜尋唯一列的鎖定列的語句,只需要索引記錄鎖定。

  • UPDATE 修改叢集索引記錄時,會在受影響的次要索引記錄上取得隱式鎖定。UPDATE 操作也會在插入新的次要索引記錄之前執行重複檢查掃描以及插入新的次要索引記錄時,在受影響的次要索引記錄上取得共用鎖定。

  • DELETE FROM ... WHERE ... 會在搜尋遇到的每個記錄上設定獨佔鍵鎖定。然而,對於使用唯一索引搜尋唯一列的鎖定列的語句,只需要索引記錄鎖定。

  • INSERT 會在插入的列上設定獨佔鎖定。此鎖定是索引記錄鎖定,而不是鍵鎖定(也就是說,沒有間隙鎖定),並且不會阻止其他工作階段插入到插入列之前的間隙中。

    在插入列之前,會設定一種稱為插入意圖間隙鎖定的間隙鎖定。此鎖定會發出插入意圖的訊號,如此一來,如果多個交易插入到同一個索引間隙中,則它們不需要互相等待,除非它們插入到該間隙內的相同位置。假設索引記錄的值為 4 和 7。嘗試插入值 5 和 6 的個別交易會在取得插入列上的獨佔鎖定之前,使用插入意圖鎖定來鎖定 4 和 7 之間的間隙,但由於列不衝突,因此不會互相阻塞。

    如果發生重複鍵錯誤,則會設定重複索引記錄上的共用鎖定。如果另一個工作階段已經具有獨佔鎖定,則如果有多個工作階段嘗試插入相同的列,則這種使用共用鎖定可能會導致死結。如果另一個工作階段刪除該列,則可能會發生這種情況。假設 InnoDB 資料表 t1 具有以下結構

    CREATE TABLE t1 (i INT, PRIMARY KEY (i)) ENGINE = InnoDB;

    現在假設三個工作階段依序執行下列操作

    工作階段 1

    START TRANSACTION;
    INSERT INTO t1 VALUES(1);

    工作階段 2

    START TRANSACTION;
    INSERT INTO t1 VALUES(1);

    工作階段 3

    START TRANSACTION;
    INSERT INTO t1 VALUES(1);

    工作階段 1

    ROLLBACK;

    工作階段 1 的第一個操作會取得該列的獨佔鎖定。工作階段 2 和 3 的操作都會導致重複鍵錯誤,並且它們都會要求該列的共用鎖定。當工作階段 1 回滾時,它會釋放該列的獨佔鎖定,並且會授與工作階段 2 和 3 排隊的共用鎖定要求。此時,工作階段 2 和 3 會發生死結:由於另一個工作階段持有的共用鎖定,因此它們都無法取得該列的獨佔鎖定。

    如果資料表已經包含鍵值為 1 的列,並且三個工作階段依序執行下列操作,則會發生類似的情況

    工作階段 1

    START TRANSACTION;
    DELETE FROM t1 WHERE i = 1;

    工作階段 2

    START TRANSACTION;
    INSERT INTO t1 VALUES(1);

    工作階段 3

    START TRANSACTION;
    INSERT INTO t1 VALUES(1);

    工作階段 1

    COMMIT;

    工作階段 1 的第一個操作會取得該列的獨佔鎖定。工作階段 2 和 3 的操作都會導致重複鍵錯誤,並且它們都會要求該列的共用鎖定。當工作階段 1 提交時,它會釋放該列的獨佔鎖定,並且會授與工作階段 2 和 3 排隊的共用鎖定要求。此時,工作階段 2 和 3 會發生死結:由於另一個工作階段持有的共用鎖定,因此它們都無法取得該列的獨佔鎖定。

  • INSERT ... ON DUPLICATE KEY UPDATE 與簡單的 INSERT 的不同之處在於,當發生重複鍵錯誤時,會在要更新的列上放置獨佔鎖定,而不是共用鎖定。對於重複的主鍵值,會取得獨佔索引記錄鎖定。對於重複的唯一鍵值,會取得獨佔鍵鎖定。

  • 如果唯一鍵沒有衝突,則 REPLACE 的運作方式與 INSERT 類似。否則,會在要取代的列上放置獨佔鍵鎖定。

  • INSERT INTO T SELECT ... FROM S WHERE ... 會在插入到 T 的每個列上設定獨佔索引記錄鎖定(沒有間隙鎖定)。如果交易隔離層級是 READ COMMITTED,則 InnoDB 會將 S 上的搜尋當做一致性讀取(沒有鎖定)。否則,InnoDB 會在 S 中的列上設定共用鍵鎖定。InnoDB 必須在後一種情況下設定鎖定:在使用以語句為基礎的二進位記錄檔進行向前復原期間,每個 SQL 語句都必須以與其最初執行方式完全相同的方式執行。

    CREATE TABLE ... SELECT ... 會使用共用鍵鎖定或作為一致性讀取來執行 SELECT,如同 INSERT ... SELECT 一樣。

    當在建構 REPLACE INTO t SELECT ... FROM s WHERE ...UPDATE t ... WHERE col IN (SELECT ... FROM s ...) 中使用 SELECT 時,InnoDB 會在資料表 s 中的列上設定共用鍵鎖定。

  • InnoDB 會在初始化資料表上先前指定的 AUTO_INCREMENT 資料行時,在與 AUTO_INCREMENT 資料行相關聯的索引結尾設定獨佔鎖定。

    使用 innodb_autoinc_lock_mode=0InnoDB 會使用特殊的 AUTO-INC 資料表鎖定模式,其中在存取自動遞增計數器時,會取得鎖定並將其保留到目前 SQL 語句結束(而不是整個交易結束)。當持有 AUTO-INC 資料表鎖定時,其他用戶端無法插入到資料表中。對於使用 innodb_autoinc_lock_mode=1大量插入,也會發生相同的行為。資料表層級的 AUTO-INC 鎖定不會與 innodb_autoinc_lock_mode=2 一起使用。如需更多資訊,請參閱第 17.6.1.6 節「InnoDB 中的 AUTO_INCREMENT 處理」

    InnoDB 會擷取先前初始化的 AUTO_INCREMENT 資料行的值,而不設定任何鎖定。

  • 如果在資料表上定義了 FOREIGN KEY 條件約束,則任何需要檢查條件約束條件的插入、更新或刪除都會在它查看以檢查條件約束的記錄上設定共用記錄層級鎖定。InnoDB 也會在條件約束失敗的情況下設定這些鎖定。

  • LOCK TABLES 會設定資料表鎖定,但是由高於 InnoDB 層的 MySQL 層設定這些鎖定。如果 innodb_table_locks = 1 (預設值) 且 autocommit = 0,則 InnoDB 會感知資料表鎖定,並且高於 InnoDB 的 MySQL 層會知道資料列層級鎖定。

    否則,InnoDB 的自動死結偵測無法偵測到涉及此類資料表鎖定的死結。此外,由於在這種情況下,高於的 MySQL 層不知道資料列層級鎖定,因此可能會在另一個工作階段目前具有資料列層級鎖定的資料表上取得資料表鎖定。但是,這不會危害交易完整性,如第 17.7.5.2 節「死結偵測」中所述。

  • 如果 innodb_table_locks=1 (預設值),則 LOCK TABLES 會在每個資料表上取得兩個鎖定。除了 MySQL 層的資料表鎖定之外,它還會取得一個 InnoDB 資料表鎖定。若要避免取得 InnoDB 資料表鎖定,請設定 innodb_table_locks=0。如果未取得 InnoDB 資料表鎖定,即使資料表的某些記錄正被其他交易鎖定,LOCK TABLES 仍會完成。

    在 MySQL 9.0 中,innodb_table_locks=0 對於使用 LOCK TABLES ... WRITE 明確鎖定的資料表沒有作用。它對於透過 LOCK TABLES ... WRITE 隱式(例如,透過觸發器)或透過 LOCK TABLES ... READ 鎖定以進行讀取或寫入的資料表有作用。

  • 當交易提交或中止時,交易持有的所有 InnoDB 鎖定都會被釋放。因此,在 autocommit=1 模式下對 InnoDB 資料表調用 LOCK TABLES 沒有太大意義,因為取得的 InnoDB 資料表鎖定會立即被釋放。

  • 您無法在交易過程中鎖定其他資料表,因為 LOCK TABLES 會執行隱式的 COMMITUNLOCK TABLES