MySQL中的事務隔離級別
- 一、事務并發問題
- 二、MySQL 事務隔離級別
- 1. READ UNCOMMITTED(讀未提交)
- 2. READ COMMITTED(讀已提交)
- 3. REPEATABLE READ(可重復讀)(MySQL 默認級別)
- 4. SERIALIZABLE(可串行化)
- 三、MySQL 默認事務隔離級別
- 四、不同隔離級別對并發問題的影響
- 五、如何選擇合適的隔離級別?
- 總結
在MySQL中,事務(Transaction)是一個執行單元,它要么完全執行,要么完全回滾,以保證數據的完整性和一致性。事務的隔離性(Isolation)是ACID特性之一,它控制了多個事務同時執行時,數據的可見性。MySQL 提供了四種事務隔離級別,每種級別都會影響事務之間的相互影響程度。
一、事務并發問題
在多個事務同時操作同一份數據時,可能會出現以下幾種并發問題:
- 臟讀(Dirty Read):一個事務讀取了另一個未提交事務修改的數據,如果后者回滾,則前者讀取到的數據是無效的。
- 不可重復讀(Non-repeatable Read):同一個事務中,執行兩次相同的查詢,由于另一個事務的提交,查詢結果不同。
- 幻讀(Phantom Read):一個事務在兩次查詢之間,另一事務插入或刪除了數據,導致前后查詢的記錄數不一致。
為了避免這些問題,SQL 標準定義了四種事務隔離級別,MySQL 也支持這四種級別。
二、MySQL 事務隔離級別
MySQL 通過 SET TRANSACTION ISOLATION LEVEL
語句來設置事務隔離級別:
SET SESSION TRANSACTION ISOLATION LEVEL <級別>;
SET GLOBAL TRANSACTION ISOLATION LEVEL <級別>;
其中 <級別>
可以是 READ UNCOMMITTED
、READ COMMITTED
、REPEATABLE READ
或 SERIALIZABLE
。
1. READ UNCOMMITTED(讀未提交)
-
特點:
- 事務可以讀取其他未提交事務的數據。
- 可能發生“臟讀”問題。
- 性能較好,因為不會使用鎖限制讀取。
-
示例:
- 事務A修改了一條記錄但尚未提交。
- 事務B在事務A提交之前讀取了修改后的數據。
- 如果事務A回滾,那么事務B讀取到的數據就是“臟數據”。
-
適用場景:
- 允許讀取未提交的數據,適用于不太關心數據一致性的應用,如日志記錄、監控數據等。
2. READ COMMITTED(讀已提交)
-
特點:
- 只能讀取已經提交的數據,避免了“臟讀”。
- 可能發生“不可重復讀”問題。
- MySQL InnoDB 通過 MVCC(多版本并發控制)來實現此隔離級別。
-
示例:
- 事務A讀取一條記錄。
- 事務B修改該記錄并提交。
- 事務A再次讀取該記錄,發現數據發生了變化(不可重復讀)。
-
適用場景:
- 適用于大多數 OLTP(在線事務處理)系統,如銀行轉賬、訂單管理系統,保證讀取的數據是已提交的但允許數據更新。
3. REPEATABLE READ(可重復讀)(MySQL 默認級別)
-
特點:
- 事務內多次讀取同一條數據時,數據保持一致(即使其他事務修改并提交了數據)。
- 通過 MVCC 實現可重復讀。
- 避免了“臟讀”和“不可重復讀”。
- 但仍然可能發生“幻讀”問題。
-
示例:
- 事務A第一次讀取某條數據。
- 事務B修改該數據并提交。
- 事務A再次讀取該數據,發現數據未改變(因為事務A讀取的是事務開始時的快照)。
-
如何解決幻讀?
- MySQL InnoDB 通過 間隙鎖(Next-Key Locking) 機制來解決幻讀問題,即鎖住范圍,使得其他事務無法插入新的數據,從而防止幻讀。
-
適用場景:
- 適用于高并發場景,尤其是金融行業,如銀行賬戶查詢和訂單管理系統。
4. SERIALIZABLE(可串行化)
-
特點:
- 最高級別的隔離,完全避免臟讀、不可重復讀和幻讀。
- 事務必須依次執行,不能并行,通常會使用 表鎖 或 行鎖。
- 并發性能極差,適用于對數據一致性要求極高的場景。
-
示例:
- 事務A讀取某一條數據,同時事務B必須等事務A完成后才能讀取或修改該數據。
-
適用場景:
- 適用于需要嚴格數據一致性的場景,如財務結算、票務系統等。
三、MySQL 默認事務隔離級別
MySQL InnoDB 存儲引擎的默認事務隔離級別是 REPEATABLE READ(可重復讀),這與 SQL 標準的默認級別(READ COMMITTED)不同。MySQL 通過 MVCC(多版本并發控制)和 間隙鎖 解決了幻讀問題,因此 REPEATABLE READ 在 MySQL 中比 SQL 標準更強。
如果想修改默認的事務隔離級別,可以在 my.cnf
(MySQL 配置文件)中修改:
[mysqld]
transaction-isolation = REPEATABLE-READ
或在運行時更改:
SET GLOBAL TRANSACTION ISOLATION LEVEL READ COMMITTED;
四、不同隔離級別對并發問題的影響
隔離級別 | 臟讀(Dirty Read) | 不可重復讀(Non-repeatable Read) | 幻讀(Phantom Read) |
---|---|---|---|
READ UNCOMMITTED | 可能發生 ? | 可能發生 ? | 可能發生 ? |
READ COMMITTED | 不會發生 ? | 可能發生 ? | 可能發生 ? |
REPEATABLE READ | 不會發生 ? | 不會發生 ? | 可能發生 ?(MySQL 中不會) |
SERIALIZABLE | 不會發生 ? | 不會發生 ? | 不會發生 ? |
五、如何選擇合適的隔離級別?
- READ UNCOMMITTED:適用于對數據一致性要求不高的場景,如日志分析、緩存數據等。
- READ COMMITTED:適用于大多數業務場景,如電商系統、用戶管理系統等,避免臟讀,提高并發性能。
- REPEATABLE READ(MySQL 默認):適用于金融系統、庫存管理,保證事務內數據的一致性,防止不可重復讀。
- SERIALIZABLE:適用于對數據一致性要求極高的場景,如銀行結算、核心財務系統,但性能損耗較大。
總結
- READ UNCOMMITTED:可能發生臟讀、不可重復讀、幻讀,性能最高但安全性最低。
- READ COMMITTED:防止臟讀,但可能發生不可重復讀和幻讀。
- REPEATABLE READ(MySQL 默認):防止臟讀和不可重復讀,MySQL 還能避免幻讀,適用于大多數高并發業務。
- SERIALIZABLE:所有并發問題都能避免,但性能最差。
MySQL 默認采用 REPEATABLE READ,主要是因為 MySQL 通過 MVCC 解決了大部分的并發問題,既能保持較高的事務隔離級別,又不會影響太多的性能。
MySQL中的事務隔離級別有四種,分別為:
讀未提交(Read Uncommitted):
- 該級別允許事務讀取其他事務尚未提交的數據(臟讀)。這意味著一個事務可以讀取到另一個事務中間狀態的數據,可能會導致數據不一致。
讀已提交(Read Committed):
- 該級別保證事務只能讀取到已提交的數據,防止臟讀。即使如此,仍然允許發生“不可重復讀”(在同一事務中兩次讀取同一數據,值可能不同,因為另一個事務已經修改了數據并提交)。
可重復讀(Repeatable Read):
- 該級別保證在一個事務中多次讀取同一數據時,結果始終一致,避免了“不可重復讀”。但是,仍然可能會出現“幻讀”(即事務讀取的結果集發生了變化,因為另一個事務插入了新的記錄)。
串行化(Serializable):
- 這是最高的隔離級別,強制事務串行執行,即事務排隊執行,一個事務在完成之前,其他事務無法訪問相同的數據。這可以完全避免臟讀、不可重復讀和幻讀,但性能會較低。