交易隔離是資料庫處理的基礎之一。隔離是縮寫 ACID 中的 I;隔離層級是在多個交易同時進行變更和執行查詢時,微調效能和可靠性、一致性以及結果可重複性之間平衡的設定。
InnoDB
提供 SQL:1992 標準描述的所有四個交易隔離層級:READ UNCOMMITTED
、READ COMMITTED
、REPEATABLE READ
和 SERIALIZABLE
。InnoDB
的預設隔離層級是 REPEATABLE READ
。
使用者可以使用 SET TRANSACTION
陳述式變更單一工作階段或所有後續連線的隔離層級。若要為所有連線設定伺服器的預設隔離層級,請在命令列或選項檔案中使用 --transaction-isolation
選項。如需隔離層級和層級設定語法的詳細資訊,請參閱第 15.3.7 節「SET TRANSACTION 陳述式」。
InnoDB
使用不同的鎖定策略來支援此處描述的每個交易隔離層級。您可以使用預設的 REPEATABLE READ
層級,針對 ACID 合規性很重要的關鍵資料操作,強制執行高度一致性。或者,您可以在大量報告等情況下,使用 READ COMMITTED
,甚至是 READ UNCOMMITTED
來放寬一致性規則,在這些情況下,精確的一致性和可重複的結果不如最小化鎖定的額外負荷重要。SERIALIZABLE
強制執行比 REPEATABLE READ
更嚴格的規則,並且主要用於特殊情況,例如使用 XA 交易,以及用於疑難排解並行處理和死鎖的問題。
以下清單描述 MySQL 如何支援不同的交易層級。清單從最常用的層級到最少用的層級。
這是
InnoDB
的預設隔離等級。在同一個交易中的一致性讀取會讀取第一次讀取所建立的快照。這表示如果您在同一個交易中發出多個普通的 (非鎖定的)SELECT
語法,這些SELECT
語法彼此之間也是一致的。請參閱第 17.7.2.3 節,「一致性非鎖定讀取」。對於鎖定讀取 (
SELECT
搭配FOR UPDATE
或FOR SHARE
)、UPDATE
和DELETE
語法,鎖定取決於語法是否使用具有唯一搜尋條件的唯一索引,或是範圍類型的搜尋條件。對於具有唯一搜尋條件的唯一索引,
InnoDB
只會鎖定找到的索引記錄,而不是它之前的間隙。對於其他搜尋條件,
InnoDB
會鎖定掃描的索引範圍,使用間隙鎖定或下一鍵鎖定來阻止其他連線將資料插入範圍涵蓋的間隙中。如需關於間隙鎖定和下一鍵鎖定的資訊,請參閱第 17.7.1 節,「InnoDB 鎖定」。
每個一致性讀取,即使在同一個交易中,都會設定並讀取其自己的新快照。如需關於一致性讀取的資訊,請參閱第 17.7.2.3 節,「一致性非鎖定讀取」。
對於鎖定讀取 (
SELECT
搭配FOR UPDATE
或FOR SHARE
)、UPDATE
語法和DELETE
語法,InnoDB
只會鎖定索引記錄,而不是它們之前的間隙,因此允許自由插入新記錄到鎖定記錄旁邊。間隙鎖定只會用於外部索引鍵限制檢查和重複索引鍵檢查。因為間隙鎖定已停用,因此可能會發生幽靈列問題,因為其他連線可以在間隙中插入新列。如需關於幽靈列的資訊,請參閱第 17.7.4 節,「幽靈列」。
只有以列為基礎的二進位記錄支援
READ COMMITTED
隔離等級。如果您使用READ COMMITTED
搭配binlog_format=MIXED
,伺服器會自動使用以列為基礎的記錄。使用
READ COMMITTED
有其他影響請考慮如下建立和填入資料的資料表
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)
對於第二個
UPDATE
,InnoDB
會執行「半一致性」讀取,將它讀取的每個列的最新已提交版本傳回 MySQL,讓 MySQL 可以判斷列是否符合UPDATE
的WHERE
條件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
隔離等級,或於執行階段變更。在執行階段,可以為所有連線全域設定,或個別針對每個連線設定。SELECT
語法會以非鎖定的方式執行,但可能會使用較早版本的列。因此,使用此隔離等級,這類讀取是不一致的。這也稱為髒讀取。否則,此隔離等級的運作方式與READ COMMITTED
相同。此等級類似
REPEATABLE READ
,但如果autocommit
已停用,InnoDB
會隱含地將所有普通SELECT
語法轉換為SELECT ... FOR SHARE
。如果autocommit
已啟用,SELECT
語法會是其自己的交易。因此,已知它是唯讀的,如果以一致性 (非鎖定) 讀取執行,則可以序列化,且不需要封鎖其他交易。(若要強制普通SELECT
在其他交易已修改選取的列時遭到封鎖,請停用autocommit
。)從 MySQL 授權表 (透過聯結清單或子查詢) 讀取資料但不修改它們的 DML 作業,不會取得 MySQL 授權表上的讀取鎖定,無論隔離等級為何。如需更多資訊,請參閱授權表並行。