文件首頁
MySQL 8.4 參考手冊
相關文件 下載本手冊
PDF (美國 Letter) - 39.9Mb
PDF (A4) - 40.0Mb
Man Pages (TGZ) - 258.5Kb
Man Pages (Zip) - 365.5Kb
Info (Gzip) - 4.0Mb
Info (Zip) - 4.0Mb


17.7.2.1 交易隔離層級

交易隔離是資料庫處理的基礎之一。隔離是縮寫 ACID 中的 I;隔離層級是在多個交易同時進行變更和執行查詢時,微調效能和可靠性、一致性以及結果可重複性之間平衡的設定。

InnoDB 提供 SQL:1992 標準描述的所有四個交易隔離層級:READ UNCOMMITTEDREAD COMMITTEDREPEATABLE READSERIALIZABLEInnoDB 的預設隔離層級是 REPEATABLE READ

使用者可以使用 SET TRANSACTION 陳述式變更單一工作階段或所有後續連線的隔離層級。若要為所有連線設定伺服器的預設隔離層級,請在命令列或選項檔案中使用 --transaction-isolation 選項。如需隔離層級和層級設定語法的詳細資訊,請參閱第 15.3.7 節「SET TRANSACTION 陳述式」

InnoDB 使用不同的鎖定策略來支援此處描述的每個交易隔離層級。您可以使用預設的 REPEATABLE READ 層級,針對 ACID 合規性很重要的關鍵資料操作,強制執行高度一致性。或者,您可以在大量報告等情況下,使用 READ COMMITTED,甚至是 READ UNCOMMITTED 來放寬一致性規則,在這些情況下,精確的一致性和可重複的結果不如最小化鎖定的額外負荷重要。SERIALIZABLE 強制執行比 REPEATABLE READ 更嚴格的規則,並且主要用於特殊情況,例如使用 XA 交易,以及用於疑難排解並行處理和死鎖的問題。

以下清單描述 MySQL 如何支援不同的交易層級。清單從最常用的層級到最少用的層級。

  • REPEATABLE READ

    這是 InnoDB 的預設隔離等級。在同一個交易中的一致性讀取會讀取第一次讀取所建立的快照。這表示如果您在同一個交易中發出多個普通的 (非鎖定的) SELECT 語法,這些 SELECT 語法彼此之間也是一致的。請參閱第 17.7.2.3 節,「一致性非鎖定讀取」

    對於鎖定讀取 (SELECT 搭配 FOR UPDATEFOR SHARE)、UPDATEDELETE 語法,鎖定取決於語法是否使用具有唯一搜尋條件的唯一索引,或是範圍類型的搜尋條件。

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

    • 對於其他搜尋條件,InnoDB 會鎖定掃描的索引範圍,使用間隙鎖定下一鍵鎖定來阻止其他連線將資料插入範圍涵蓋的間隙中。如需關於間隙鎖定和下一鍵鎖定的資訊,請參閱第 17.7.1 節,「InnoDB 鎖定」

  • READ COMMITTED

    每個一致性讀取,即使在同一個交易中,都會設定並讀取其自己的新快照。如需關於一致性讀取的資訊,請參閱第 17.7.2.3 節,「一致性非鎖定讀取」

    對於鎖定讀取 (SELECT 搭配 FOR UPDATEFOR SHARE)、UPDATE 語法和 DELETE 語法,InnoDB 只會鎖定索引記錄,而不是它們之前的間隙,因此允許自由插入新記錄到鎖定記錄旁邊。間隙鎖定只會用於外部索引鍵限制檢查和重複索引鍵檢查。

    因為間隙鎖定已停用,因此可能會發生幽靈列問題,因為其他連線可以在間隙中插入新列。如需關於幽靈列的資訊,請參閱第 17.7.4 節,「幽靈列」

    只有以列為基礎的二進位記錄支援 READ COMMITTED 隔離等級。如果您使用 READ COMMITTED 搭配 binlog_format=MIXED,伺服器會自動使用以列為基礎的記錄。

    使用 READ COMMITTED 有其他影響

    • 對於 UPDATEDELETE 語法,InnoDB 只會為其更新或刪除的列保留鎖定。在 MySQL 評估 WHERE 條件後,會釋放不符合列的記錄鎖定。這大大降低了死鎖的可能性,但死鎖仍然可能發生。

    • 對於 UPDATE 語法,如果列已被鎖定,InnoDB 會執行半一致性讀取,將最新的已提交版本傳回 MySQL,讓 MySQL 可以判斷列是否符合 UPDATEWHERE 條件。如果列符合 (必須更新),MySQL 會再次讀取該列,而這次 InnoDB 會鎖定該列或等待該列上的鎖定。

    請考慮如下建立和填入資料的資料表

    CREATE TABLE t (a INT NOT NULL, b INT) ENGINE = InnoDB;
    INSERT INTO t VALUES (1,2),(2,3),(3,2),(4,3),(5,2);
    COMMIT;

    在此情況下,資料表沒有索引,因此搜尋和索引掃描會使用隱藏的叢集索引進行記錄鎖定 (請參閱第 17.6.2.1 節,「叢集索引和次要索引」),而不是索引的欄。

    假設一個連線使用這些語法執行 UPDATE

    # Session A
    START TRANSACTION;
    UPDATE t SET b = 5 WHERE b = 3;

    也假設第二個連線執行在第一個連線執行之後的這些語法來執行 UPDATE

    # Session B
    UPDATE t SET b = 4 WHERE b = 2;

    InnoDB 執行每個 UPDATE 時,它會先取得每個列的互斥鎖定,然後判斷是否要修改它。如果 InnoDB 不修改該列,則會釋放鎖定。否則,InnoDB 會保留鎖定直到交易結束。這會以下列方式影響交易處理。

    當使用預設的 REPEATABLE READ 隔離等級時,第一個 UPDATE 會在它讀取的每個列上取得 x 鎖定,且不會釋放任何鎖定

    x-lock(1,2); retain x-lock
    x-lock(2,3); update(2,3) to (2,5); retain x-lock
    x-lock(3,2); retain x-lock
    x-lock(4,3); update(4,3) to (4,5); retain x-lock
    x-lock(5,2); retain x-lock

    第二個 UPDATE 會在嘗試取得任何鎖定時立即遭到封鎖 (因為第一個更新已保留所有列上的鎖定),且直到第一個 UPDATE 提交或復原後才會繼續進行

    x-lock(1,2); block and wait for first UPDATE to commit or roll back

    如果改用 READ COMMITTED,第一個 UPDATE 會在它讀取的每個列上取得 x 鎖定,並釋放它不修改的列的鎖定

    x-lock(1,2); unlock(1,2)
    x-lock(2,3); update(2,3) to (2,5); retain x-lock
    x-lock(3,2); unlock(3,2)
    x-lock(4,3); update(4,3) to (4,5); retain x-lock
    x-lock(5,2); unlock(5,2)

    對於第二個 UPDATEInnoDB 會執行半一致性讀取,將它讀取的每個列的最新已提交版本傳回 MySQL,讓 MySQL 可以判斷列是否符合 UPDATEWHERE 條件

    x-lock(1,2); update(1,2) to (1,4); retain x-lock
    x-lock(2,3); unlock(2,3)
    x-lock(3,2); update(3,2) to (3,4); retain x-lock
    x-lock(4,3); unlock(4,3)
    x-lock(5,2); update(5,2) to (5,4); retain x-lock

    但是,如果 WHERE 條件包含索引的欄,且 InnoDB 使用該索引,則只會在取得和保留記錄鎖定時考慮索引的欄。在下列範例中,第一個 UPDATE 會在 b = 2 的每個列上取得並保留 x 鎖定。第二個 UPDATE 會在嘗試取得相同記錄上的 x 鎖定時遭到封鎖,因為它也會使用在欄 b 上定義的索引。

    CREATE TABLE t (a INT NOT NULL, b INT, c INT, INDEX (b)) ENGINE = InnoDB;
    INSERT INTO t VALUES (1,2,3),(2,2,4);
    COMMIT;
    
    # Session A
    START TRANSACTION;
    UPDATE t SET b = 3 WHERE b = 2 AND c = 3;
    
    # Session B
    UPDATE t SET b = 4 WHERE b = 2 AND c = 4;

    可以於啟動時設定 READ COMMITTED 隔離等級,或於執行階段變更。在執行階段,可以為所有連線全域設定,或個別針對每個連線設定。

  • READ UNCOMMITTED

    SELECT 語法會以非鎖定的方式執行,但可能會使用較早版本的列。因此,使用此隔離等級,這類讀取是不一致的。這也稱為髒讀取。否則,此隔離等級的運作方式與 READ COMMITTED 相同。

  • SERIALIZABLE

    此等級類似 REPEATABLE READ,但如果 autocommit 已停用,InnoDB 會隱含地將所有普通 SELECT 語法轉換為 SELECT ... FOR SHARE。如果 autocommit 已啟用,SELECT 語法會是其自己的交易。因此,已知它是唯讀的,如果以一致性 (非鎖定) 讀取執行,則可以序列化,且不需要封鎖其他交易。(若要強制普通 SELECT 在其他交易已修改選取的列時遭到封鎖,請停用 autocommit。)

    從 MySQL 授權表 (透過聯結清單或子查詢) 讀取資料但不修改它們的 DML 作業,不會取得 MySQL 授權表上的讀取鎖定,無論隔離等級為何。如需更多資訊,請參閱授權表並行