一致性讀取表示 InnoDB
使用多版本控制,在某個時間點向查詢呈現資料庫的快照。查詢會看到該時間點之前已提交的交易變更,而不會看到之後或未提交交易的任何變更。此規則的例外情況是,查詢會看到同一個交易中較早的陳述式所做的變更。此例外情況會導致以下異常:如果您更新表格中的某些列,則 SELECT
會看到已更新列的最新版本,但它也可能會看到任何列的較舊版本。如果其他工作階段同時更新相同的表格,則此異常表示您可能會在資料庫中看到從未存在過的表格狀態。
如果交易隔離級別為 REPEATABLE READ
(預設級別),則相同交易中的所有一致性讀取都會讀取該交易中第一次讀取所建立的快照。您可以透過提交目前的交易,然後發出新的查詢,來為您的查詢取得較新的快照。
使用 READ COMMITTED
隔離級別,交易中的每個一致性讀取都會設定並讀取其自己的新快照。
一致性讀取是 InnoDB
在 READ COMMITTED
和 REPEATABLE READ
隔離級別中處理 SELECT
陳述式的預設模式。一致性讀取不會在其存取的表格上設定任何鎖定,因此,當在表格上執行一致性讀取時,其他工作階段可以自由地同時修改這些表格。
假設您正在預設的 REPEATABLE READ
隔離層級中執行。當您發出一致性讀取(也就是說,一個普通的 SELECT
陳述式)時,InnoDB
會根據您的查詢所看到的資料庫狀態,給予您的交易一個時間點。如果另一個交易在您的時間點被指定之後刪除了一列並提交,您不會看到該列已被刪除。插入和更新的處理方式也類似。
資料庫狀態的快照適用於交易中的 SELECT
陳述式,不一定適用於 DML 陳述式。如果您插入或修改了一些列,然後提交該交易,則從另一個並行的 REPEATABLE READ
交易發出的 DELETE
或 UPDATE
陳述式可能會影響那些剛提交的列,即使該會話無法查詢它們。如果一個交易更新或刪除由另一個不同交易提交的列,這些變更會對目前的交易可見。例如,您可能會遇到以下情況:
SELECT COUNT(c1) FROM t1 WHERE c1 = 'xyz';
-- Returns 0: no rows match.
DELETE FROM t1 WHERE c1 = 'xyz';
-- Deletes several rows recently committed by other transaction.
SELECT COUNT(c2) FROM t1 WHERE c2 = 'abc';
-- Returns 0: no rows match.
UPDATE t1 SET c2 = 'cba' WHERE c2 = 'abc';
-- Affects 10 rows: another txn just committed 10 rows with 'abc' values.
SELECT COUNT(c2) FROM t1 WHERE c2 = 'cba';
-- Returns 10: this txn can now see the rows it just updated.
您可以透過提交您的交易,然後執行另一個 SELECT
或 START TRANSACTION WITH CONSISTENT SNAPSHOT
來推進您的時間點。
這稱為多版本並行控制。
在以下範例中,只有當 B 提交插入並且 A 也提交時,會話 A 才會看到 B 插入的列,如此時間點才會推進到 B 的提交之後。
Session A Session B
SET autocommit=0; SET autocommit=0;
time
| SELECT * FROM t;
| empty set
| INSERT INTO t VALUES (1, 2);
|
v SELECT * FROM t;
empty set
COMMIT;
SELECT * FROM t;
empty set
COMMIT;
SELECT * FROM t;
---------------------
| 1 | 2 |
---------------------
如果您想要查看資料庫的「最新」狀態,請使用 READ COMMITTED
隔離層級或鎖定讀取。
SELECT * FROM t FOR SHARE;
使用 READ COMMITTED
隔離層級,交易中的每個一致性讀取都會設定並讀取其自己的最新快照。使用 FOR SHARE
,會發生鎖定讀取:SELECT
會封鎖,直到包含最新列的交易結束(請參閱第 17.7.2.4 節,「鎖定讀取」)。
一致性讀取不適用於某些 DDL 陳述式
一致性讀取不適用於
DROP TABLE
,因為 MySQL 無法使用已刪除的資料表,而且InnoDB
會銷毀該資料表。一致性讀取不適用於
ALTER TABLE
操作,這些操作會建立原始資料表的暫時副本,並在建立暫時副本時刪除原始資料表。當您在交易中重新發出一致性讀取時,新資料表中的列是不可見的,因為當交易的快照建立時,這些列並不存在。在這種情況下,交易會傳回錯誤:ER_TABLE_DEF_CHANGED
,「資料表定義已變更,請重試交易」。
對於諸如 INSERT INTO ... SELECT
、UPDATE ... (SELECT)
和 CREATE TABLE ... SELECT
等子句中的 SELECT,其讀取類型會有所不同,這些子句未指定 FOR UPDATE
或 FOR SHARE
。
預設情況下,
InnoDB
會對這些陳述式使用更強的鎖定,並且SELECT
部分的作用類似於READ COMMITTED
,其中每個一致性讀取(即使在同一交易中)都會設定並讀取其自己的最新快照。若要在這種情況下執行非鎖定讀取,請將交易的隔離層級設定為
READ UNCOMMITTED
或READ COMMITTED
,以避免在從選取的資料表讀取的列上設定鎖定。