文件首頁
MySQL 8.4 參考手冊
相關文件 下載本手冊
PDF (美式信紙) - 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.3 一致性非鎖定讀取

一致性讀取表示 InnoDB 使用多版本控制,向查詢呈現資料庫在某個時間點的快照。查詢會看到在該時間點之前提交的交易變更,而看不到之後或未提交交易的變更。此規則的例外情況是,查詢會看到相同交易內較早陳述式所做的變更。此例外情況會導致下列異常:如果您更新表格中的某些列,則 SELECT 會看到更新列的最新版本,但它也可能會看到任何列的較舊版本。如果其他會話同時更新相同的表格,則異常表示您可能會看到表格處於資料庫中從未存在的狀態。

如果交易隔離層級REPEATABLE READ (預設層級),則相同交易內的所有一致性讀取都會讀取該交易中第一個此類讀取所建立的快照。您可以藉由提交目前的交易,然後發出新的查詢,來取得查詢的更新快照。

READ COMMITTED 隔離層級下,交易內的每個一致性讀取都會設定並讀取其自己的新快照。

一致性讀取是 InnoDB 處理 SELECT 語句在 READ COMMITTEDREPEATABLE READ 隔離層級中的預設模式。一致性讀取不會在其存取的表格上設定任何鎖定,因此其他連線可以自由地在表格上執行一致性讀取時同時修改這些表格。

假設您正在預設的 REPEATABLE READ 隔離層級中執行。當您發出一致性讀取(即,普通的 SELECT 語句)時,InnoDB 會根據您的查詢所看到的資料庫,給予您的交易一個時間點。如果另一個交易在您的時間點被指派後刪除一列並提交,您不會看到該列已被刪除。插入和更新的處理方式類似。

注意

資料庫狀態的快照適用於交易中的 SELECT 語句,但不一定適用於 DML 語句。如果您插入或修改一些列,然後提交該交易,則從另一個並行的 REPEATABLE READ 交易發出的 DELETEUPDATE 語句可能會影響那些剛提交的列,即使該連線無法查詢它們。如果交易確實更新或刪除由另一個交易提交的列,這些變更會對目前的交易可見。例如,您可能會遇到如下情況

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.

您可以通過提交交易,然後執行另一個 SELECTSTART TRANSACTION WITH CONSISTENT SNAPSHOT 來推進您的時間點。

這稱為多版本並行控制

在以下範例中,連線 A 只有在 B 提交插入,且 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 ... SELECTUPDATE ... (SELECT)CREATE TABLE ... SELECT 等子句中的 select 讀取類型會有所不同,這些子句未指定 FOR UPDATEFOR SHARE

  • 預設情況下,InnoDB 會對這些語句使用更強的鎖定,而且 SELECT 部分的行為就像 READ COMMITTED,其中每個一致性讀取(即使在同一個交易內)都會設定並讀取自己的新快照。

  • 為了在這種情況下執行非鎖定讀取,請將交易的隔離層級設定為 READ UNCOMMITTEDREAD COMMITTED,以避免在從選定的表格讀取列時設定鎖定。