啥是 MySQL 事務隔離級別?

detectiveHLH發表於2021-07-26

之前發過一篇文章,簡單瞭解 MySQL 中相關的鎖,裡面提到了,如果我們使用的 MySQL 儲存引擎為 InnoDB ,並且其事務隔離級別是 RR 可重複讀的話,是可以避免幻讀的。

但是沒想到,都 1202 年了都還有人槓,說 InnoDB 的 RR 隔離級別下會出現幻讀,只能依靠 gap 和 next-key 這兩個鎖來防止幻讀 ,最開始我還以為是他真的不知道這個點,就跟他聊,最後聊下來發現,發現是在鑽牛角尖。

這個在下面講到 可重複讀 的隔離級別時會講。

本來我覺得事務隔離級別這玩意兒太簡單沒啥可講的,但是經過了上面這件事,我打算詳細的把事務隔離給講講。接下來順便就把 InnoDB 所有的事務隔離級別給摟一遍。

ACID

在聊事務隔離級別之前,我們需要知道 ACID 模型。

ACID 模型
ACID 模型

分別代表:

  • Atomicity 原子性
  • Consistency 一致性
  • Isolation 隔離型
  • Durability 永續性

原子性,代表 InnoDB 事務中,所有的操作要麼全部成功,要麼全部失敗,不會處於某個中間狀態。說的更通俗一點,如果事務 A 失敗,其所做的所有的更改應該全部回滾。

一致性,主要是保護資料的一致性,防止由於資料庫的崩潰而導致的資料一致性問題。舉個例子,我們更新 MySQL 的資料,更新的資料會先到 InnoDB 的 Buffer Pool 中,如果此時 MySQL 所在的機器突然意外重啟了,如果 InnoDB 沒有崩潰恢復機制,之前更新的資料就會丟失,資料的一致性問題就出現了。

很多其他的部落格寫的是事務開要始前後,資料的完整性沒有被破壞。我表示看了根本看不懂,太抽象了。

隔離性,主要是指事務之間的隔離,再具體一點,就是我們本篇文章要討論的事務隔離級別了。

永續性,主要是指我們新增或者刪除了某些資料,一旦成功,這些操作應該需要被持久化到磁碟上去。

ACID 模型可以理解成資料庫的設計正規化,主要關注點在資料資料、及其本身的可靠性。而 MySQL 中的 InnoDB 就完全遵守 ACID 模型,並且在儲存引擎層就支援資料一致性的校驗和崩潰恢復的機制。

而 ACID 中的隔離型,就是我們這篇文章中討論的重點。

事務隔離級別

有很多文章上來就直接介紹事務隔離級別的種類,這個種類啥意思,那個種類怎麼用。但我認為應該先了解為什麼需要事務隔離級別,以及事務隔離級別到底解決了什麼問題,這才是關鍵。

我們知道 InnoDB 中同時會有多個事務對資料進行操作,舉一些例子:

  • 假如事務A需要查詢 id=1 的資料,但是事務A查詢完畢之後,事務B對 id=1 的資料做了更新,那此時事務A再次執行查詢,應該看到更新前的資料還是更新後的資料?
  • 或者還是上面那個例子,事務A讀取了事務B的資料,但是如果事務B進行回滾了怎麼辦?事務A的資料不就變成了髒資料?
  • 又或者事務A讀取了 1-3點 的日程安排,有4條,但是事務A讀取完成後事務B又向 1-3 點這個時間段插入了一條新的安排,那麼事務A如果再次讀取,應該顯示4條日程安排還是5條?

以上的這些問題,就需要事務隔離級別來回答了。其實以上的三種情況分別對應不可重複讀髒讀幻讀。InnoDB 通過事務隔離級別分別的解決了上面的問題。所有的事務隔離級別如下:

  • READ UNCOMMITTED 讀未提交
  • READ COMMITTED 讀已提交
  • REPEATABLE READ 可重複讀
  • SERIALIZABLE 序列化

InnoDB 預設的事務隔離級別為 REPEATABLE READ

讀未提交

事務A讀取了事務B還未提交的資料

如果事務B此時出錯了進行了回滾,那麼事務A讀取到的資料就成為了髒資料,從而造成髒讀

如果事務B又更新事務A讀取的資料,那麼事務A再次讀取,讀取到了事務B修改的結果,這造成了不可重複讀

而如果事務B又新增了資料,事務A再次讀取,會讀取到事務B新增的資料,這造成了幻讀

所以總結來說,在讀未提交這個隔離級別下,會造成以下的問題:

  • 髒讀
  • 不可重複讀
  • 幻讀

讀已提交

事務A讀取了事務B已經提交的資料

如果事務B更新了事務A讀取到的資料,並且提交,那麼當事務A再次進行讀取,就會讀取到其他事務的變更,就造成了不可重複讀

同理,如果事務B新增了資料並且提交,事務A再次進行讀取時拿到了事務B剛剛提交的資料,這就造成了幻讀。

所以總結來說,在讀已提交的隔離級別下,會造成:

  • 不可重複讀
  • 幻讀

可重複讀

事務A不會讀取到事務B更新的資料,也不會讀到事務B新增的資料

在可重複讀場景下,不會出現髒讀、不會出現不可重複讀,可能會出現幻讀

無論事務B做了什麼操作,事務A查詢到的 id=1 的資料都是張三。

但是,在某些情況下,還是可能會出現 幻讀可重複讀 只是在某些情況下會產生幻讀,但絕對不是 InnoDB 無法避免幻讀。首先,InnoDB 在 RR 隔離級別下有很明確的解決幻讀的方式,那就是——臨鍵鎖,一種組合了 gap 鎖和記錄鎖的鎖。

接下來舉個例子來看在 RR 隔離級別下,什麼情況會出現幻讀,什麼情況下不會出現幻讀。首先是 可能會出現幻讀

SELECT * FROM `student` WHERE `id` > 1

由於 InnoDB 有 MVCC 來進行多事務的併發,此時 SELECT 走的是快照讀,不會加鎖,那麼臨鍵鎖就無法發揮其作用,如果有其他事務插入了一條資料,那麼事務再次執行上面的語句是有可能會查出 id > 1 的資料。

但是如果顯示的進行加鎖,則可以避免這個問題。

SELECT * FROM `student` WHERE `id` > 1 FOR UPDATE

至於為什麼臨鍵鎖可以避免幻讀,之前的文章已經聊的很清楚,就不在此贅述了。

序列化

所以事務被強制的序列執行

這樣從根本上就避免了併發的問題,但是這樣會使得 MySQL 的效能下降。因為現在同一時間只能有一個事務在執行。

EOF

關於事務隔離級別就先介紹到這,之後有時間了就把 事務隔離級別 的底層原理給摟一遍。

本篇文章已放到我的 Github github.com/sh-blog 中,歡迎 Star。微信搜尋關注【SH的全棧筆記】,回覆【佇列】獲取MQ學習資料,包含基礎概念解析和RocketMQ詳細的原始碼解析,持續更新中。

如果你覺得這篇文章對你有幫助,還麻煩點個贊關個注分個享留個言

相關文章