MySQL 的事務隔離級別定義了多個并發事務在訪問和修改相同數據時,彼此之間的可見性和影響程度。它解決了并發事務可能引發的三類核心問題:
- 臟讀: 一個事務讀取了另一個未提交事務修改的數據。
- 不可重復讀: 一個事務內多次讀取同一行數據,由于其他事務的修改并提交,導致前后讀取的結果不一致。
- 幻讀: 一個事務內多次執行相同的查詢,由于其他事務的插入或刪除并提交,導致前后查詢結果集的行數不一致。
MySQL 遵循 SQL 標準,支持以下四種事務隔離級別,按隔離強度從低到高排序:
-
READ UNCOMMITTED (讀未提交):
- 描述: 最低的隔離級別。
- 解決的問題: 無。
- 可能存在的問題: 臟讀、不可重復讀、幻讀都可能發生。
- 原理: 一個事務可以看到其他事務尚未提交的修改。
- 使用場景: 非常少見,通常只在需要查看最新可能數據(即使未提交)且完全不在意數據一致性的極端場景使用。性能最高但風險最大。
-
READ COMMITTED (讀已提交):
- 描述: 大多數數據庫系統(如 Oracle, SQL Server, PostgreSQL)的默認隔離級別(但 MySQL InnoDB 默認不是它)。
- 解決的問題: 臟讀。
- 可能存在的問題: 不可重復讀、幻讀。
- 原理: 一個事務只能看到其他事務已經提交的修改。它保證讀取到的任何數據都是已提交的數據。
- 實現 (InnoDB): 通常使用一致性非鎖定讀(快照讀)。在
SELECT
語句執行時(或事務內第一次讀時)創建該語句的快照(Read View),基于這個快照讀取數據。其他事務的提交在本次查詢執行后對本事務的后續查詢才可見(除非使用FOR UPDATE
/LOCK IN SHARE MODE
加鎖)。 - 使用場景: 比
READ UNCOMMITTED
安全很多,是很多應用的合理選擇,特別是當應用邏輯能夠容忍不可重復讀和幻讀時。性能較好。
-
REPEATABLE READ (可重復讀):
- 描述: MySQL InnoDB 存儲引擎的默認隔離級別。
- 解決的問題: 臟讀、不可重復讀。
- 可能存在的問題: 幻讀(但在 InnoDB 中,通過
Next-Key Locks
機制在大多數情況下避免了幻讀)。 - 原理: 保證在同一個事務中多次讀取同一行數據的結果是一致的。
- 實現 (InnoDB):
- 一致性非鎖定讀 (快照讀): 在事務中第一次執行
SELECT
語句時創建一個整個事務的一致性快照(Read View)。在該事務后續的所有普通SELECT
操作都會基于這個同一個快照來讀取數據。因此,即使其他事務修改并提交了數據,本事務內看到的仍然是它開始時那個“快照”版本的數據,從而避免了不可重復讀。 - 鎖定讀 (當前讀): 當執行
SELECT ... FOR UPDATE
、SELECT ... LOCK IN SHARE MODE
、UPDATE
、DELETE
語句時,InnoDB 會使用Next-Key Locks。這種鎖不僅鎖住掃描到的索引記錄,還會鎖住這些記錄之前的“間隙”,防止其他事務在鎖定的范圍內插入新行。正是這種機制在很大程度上防止了幻讀的發生。
- 一致性非鎖定讀 (快照讀): 在事務中第一次執行
- 使用場景: 需要保證事務內多次讀取相同數據結果一致的場景(如對賬、報表生成)。是 MySQL InnoDB 的默認且推薦級別,在保證較高一致性同時提供較好性能。
-
SERIALIZABLE (可串行化):
- 描述: 最高的隔離級別。
- 解決的問題: 臟讀、不可重復讀、幻讀。
- 可能存在的問題: 性能最低(并發度最低),可能導致大量的鎖等待甚至死鎖。
- 原理: 通過強制事務串行執行(而非并發執行)來避免所有并發問題。它會在讀取的每一行數據上都加鎖(通常是共享鎖)。
- 實現 (InnoDB): 將所有的普通
SELECT
語句隱式轉換為SELECT ... LOCK IN SHARE MODE
,即對讀取的數據加共享鎖。這會導致其他事務無法修改這些數據,寫操作會被阻塞。寫操作(UPDATE
,DELETE
,INSERT
)仍然會對涉及的行加排他鎖。 - 使用場景: 要求最高級別的數據一致性,且可以接受顯著性能下降的場景。如金融核心交易等。
總結對比表:
隔離級別 | 臟讀 | 不可重復讀 | 幻讀 (InnoDB) | 性能 | 并發度 |
---|---|---|---|---|---|
READ UNCOMMITTED | ? | ? | ? | 最高 | 最高 |
READ COMMITTED | ? | ? | ? | 較高 | 較高 |
REPEATABLE READ | ? | ? | 大部分避免 (Next-Key Lock) | 中等 | 中等 |
SERIALIZABLE | ? | ? | ? | 最低 | 最低 |
查看和設置隔離級別:
-
查看當前會話隔離級別:
SELECT @@transaction_isolation; -- MySQL 8.0+ -- 或 SELECT @@tx_isolation; -- MySQL 5.x
-
查看全局隔離級別:
SELECT @@global.transaction_isolation; -- MySQL 8.0+ -- 或 SELECT @@global.tx_isolation; -- MySQL 5.x
-
設置當前會話隔離級別 (僅影響當前連接):
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED; -- 替換為需要的級別
-
設置全局隔離級別 (影響之后的所有新連接,需要相應權限):
SET GLOBAL TRANSACTION ISOLATION LEVEL READ COMMITTED; -- 替換為需要的級別
-
在啟動時設置 (修改配置文件
my.cnf
/my.ini
):[mysqld] transaction-isolation = READ-COMMITTED # 替換為需要的級別
選擇建議:
- 優先使用默認 (
REPEATABLE READ
): MySQL InnoDB 的默認REPEATABLE READ
在性能和一致性之間取得了很好的平衡,并通過Next-Key Locks
有效避免了幻讀,適合絕大多數應用場景。 - 需要更強實時性且容忍不可重復讀/幻讀: 考慮
READ COMMITTED
。這在某些需要看到其他事務最新提交結果的場景(如消息通知)可能更合適。 - 最高一致性要求: 僅在絕對必要且完全理解性能代價時使用
SERIALIZABLE
。 - 避免使用
READ UNCOMMITTED
: 除非有非常特殊且可控的場景需求。
理解事務隔離級別對于設計高性能、高一致性的數據庫應用至關重要。務必根據你的具體應用需求來選擇最合適的隔離級別。