
一段廢(huì)話(fà)
前面一直在寫 JVM 系列的文章,直到有一天,卡殼了,後面不知道寫啥了,緣由就是筆者是一個菜鳥(公衆號名稱就能看出),懂得少,理解也不夠透徹,致使差很少快兩個月沒更了(主要仍是由於懶)。mysql
最近打開微信公衆號後臺一看,發現停更兩月,竟然漲了 200 多個粉(上次春節也是由於懶,停更了快 3 個月,掉了 400 多個粉),內心一喜,決定仍是繼續堅持下去(程序員的快樂就是這麼簡單),不過 JVM 系列先暫停一段時間,而是接着以前分享過的 MySQL 系列繼續(不出意外,接下來幾篇將主要分享 MySQL 系列和 Redis 系列的知識)。程序員
先來篇基礎一點的文章,找找感受,今天講講事務的隔離級別,雖然很基礎,可是面試時卻常常被問到(初級攻城獅),因此仍是頗有必要了解一下,並且後面計劃分享的兩篇文章,和事務的隔離級別息息相關。web
4 種現象與 4 種隔離級別
衆所周知,一般關係型數據庫在高併發面前,很弱雞,可是吧,它即便再弱雞,在正常的使用過程當中,也存在一些併發的場景。而一出現併發,就會出現各類各樣的問題,例如:髒寫、髒讀、不可重複讀、幻讀,這都是些啥呢?下面一一具體舉例解釋。
髒寫現象與讀未提交(read uncommitted)隔離級別
事務 A 和事務 B 同時執行,它們都要對同一條數據進行修改(這條數據的值,假設爲 data)。sql
-
首先是事務 A 將數據的值修改成 data1,暫時不提交事務; -
而後事務 B 將數據的值修改成 data2,而後立馬提交事務; -
事務 A 可能因爲本身的業務系統出現了異常,所以進行回滾操做,將數據的值從新回滾爲 data。
在這個過程當中,事務 A 一個回滾操做,將事務 B 修改的值也回滾了,一晚上回到解放前,事務 B 白忙活一場,這種現象叫作髒寫。它的本質就是一個事務將另外一個事務提交的修改操做回滾了。數據庫
顯然在數據庫中,確定不容許這種現象存在。那麼該如何解決這種問題呢?加個寫鎖就能解決,要對數據修改,必需要先獲取到這行數據的寫鎖,不然不能修改。微信
在事務 A 開啓時,對 data 加上寫鎖,直到事務 A 提交事務之後,才釋放鎖,在此期間,其餘事務因爲獲取不到鎖,也就談不上對 data 數據進行修改了。併發
在實際的數據庫中,則是將事務的隔離級別設置爲讀未提交(read uncommitted) ,在該隔離級別下,能保證事務提交以前,其餘事務不能同時對這條數據進行修改。編輯器
髒讀現象與讀提交(read committed)隔離級別
事務 A 和事務 B 同時執行,事務 A 先將數據從 data 修改成 data1,而後暫時不提交事務,而是繼續向後處理業務邏輯。高併發
而後事務 B 讀取這一行數據,讀取到值爲 data1,而後基於 data1 這個值去處理本身的業務邏輯了。
接着事務 A 在處理後面的業務邏輯時出現了異常,所以要進行回滾操做,將數據從 data1 回滾爲 data。
在這個過程當中,當事務 B 再去查詢時發現數據的值爲 data,這就蛋疼了,原本是基於 data1 這個值去作的業務邏輯處理,結果如今發現值倒是 data,完蛋了,全 NM 錯了,這種現象就是髒讀,它的本質就是一個事務讀到了另外一個事務未提交的值。
爲了解決髒讀的問題,數據庫中定義了讀提交(read committed) 隔離級別,它的意思就是,在讀數據的時候,只能讀到別的事務提交事後的值,對於未提交的事務對數據所作的修改操做,當前事務是沒法讀取到的。
在讀提交的事務隔離級別下,當事務 B 去讀取數據時,發現事務 A 尚未提交,所以它不能讀取到 data1 這個值,只能讀取到 data 這個值。
不可重複讀現象與可重複讀(repeatable read)隔離級別
假設如今數據庫中事務的隔離級別爲讀提交,也就是未提交的事務修改的值,其餘事務是讀取不到的,那麼在當前事務隔離級別下還會有其餘問題嗎?
事務 A 和事務 B 同時開啓事務,事務 A 先從數據庫查詢數據,讀取到的值爲 data,而後事務 A 先不提交事務。
接着事務 B 修改數據,將數據的值從 data 修改成 data1。
若是事務 B 先不提交事務,那麼事務 A 此時來讀取數據時,能讀取到最新的值 data1 嗎?不能,由於咱們假設了此時事務的隔離級別處於讀提交狀態。
好,既然不能事務 A 不能讀取到最新值,那麼如今事務 B 提交事務,接着讓事務 A 再次從數據庫查詢數據,此時能讀取到最新的值嗎?
能,此時讀取到的值爲 data1,由於事務 B 已經提交了事務,在讀提交的隔離級別下,提交了的事務,其餘事務都能讀取到最新的值。
可是這有問題啊!在同一個事務內(事務 A),它讀取了兩次數據,發現先後兩次讀取到的值分別是 data 和 data1,同一行數據,讀到的值卻不同,事務 A 此時內心可能 MMP 了,幹啥啊,忽悠我呢!
實際上這就是不可重複讀現象,它的本質就是在同一個事務內,屢次從數據庫讀取數據,讀取到的值不同。(注意不可重複讀與髒讀的區別:髒讀是指讀到了未提交事務的值,不可重複讀指的是其餘事務更新數據並提交後,本身先後讀取到的數據不一致)
所以,可重複讀(repeatable read) 事務隔離級別出現了,它的意思是,在同一個事務內,例如事務 A,屢次從數據庫讀取數據時,每次讀取到的值是同樣的,即便在此期間有其餘事務修改了這條數據的值,也不會致使事務 A 先後兩次讀取到的值不同。
幻讀現象與串行化(serializable)隔離級別
假設事務 A 和事務 B 併發執行,首先事務 A 先執行了以下 SQL,假設查到了 1 條數據;
### 假設只查詢出來一條數據
select * from t where id > 1
接着事務 B 向數據庫中又新插入了 10 條數據,並提交了事務;
而後事務 A 又使用一樣的 SQL 語句查詢數據,這時會查詢出來 11 條數據,比以前查出來的數據多,也就是說看到了更多的數據,這種現象就是幻讀。
注意幻讀與不可重複讀的區別:幻讀特指在同一個事務內,先後兩次查詢,後面的查詢,讀到了以前沒看到的數據;而不可重複讀指的是在同一個事務內,針對同一行數據而言,先後兩次查詢,讀取到的值不同。
爲了解決幻讀的問題,數據庫提出了串行化(Serializable) 這種事務隔離級別。
那麼什麼是串行化呢?歸根結底,出現髒寫、髒讀、不可重複讀、幻讀這些問題,都是由於併發致使的,那要一會兒所有解決這些問題,最簡單的辦法就是不要讓線程併發執行,讓多個線程一個一個執行,也就是串行化(也就是不讓併發出現,都沒有併發了,也就沒有髒寫、髒讀、不可重複讀、幻讀這些幺蛾子了)。
總結
因爲事務的併發執行,會形成不少異常的現象,例如髒寫、髒讀、不可重複讀、幻讀等。
這四種現象總結起來就是:
-
髒寫指的是一個事務將其餘事務提交的修改回滾了; -
髒讀指的是一個事務讀取到了另外一個事務未提交的修改值; -
不可重複讀指的是一個事務對同一條數據,兩次先後讀取到的值不同,這是由於在此期間有其餘事務更新了該條數據; -
幻讀指的是一個事務,後一次的查詢比前一次查詢看到的數據多了,它特指讀到了新的數據,須要與不可重複讀的現象區分開來。
爲了解決這些問題,SQL 標準(注意:這裏說的是 SQL 標準)中定義了 4 種事務的隔離級來應對這些現象,分別是:讀未提交、讀提交、可重複讀、串行化,它們的強度也依次遞增。在這四種隔離級別下,它們的表現以下:
髒寫 | 髒讀 | 不可重複讀 | 幻讀 | |
---|---|---|---|---|
讀未提交 | ❌ | ✅ | ✅ | ✅ |
讀提交 | ❌ | ❌ | ✅ | ✅ |
可重複讀 | ❌ | ❌ | ❌ | ✅ |
串行化 | ❌ | ❌ | ❌ | ❌ |
實際上,這四種事務的隔離級別只是 SQL 標準中定義的,在各大數據庫中,這 4 種隔離級別在實現細節上又有所不一樣,例如:對於 MySQL 的 InnoDB 存儲引擎而言,在可重複讀隔離級別下,MySQL 經過 MVCC 機制解決了幻讀的問題(在 SQL 標準中,可重複讀隔離級別下仍存在幻讀的問題)。
那麼 MySQL 是如何經過 MVCC 機制解決幻讀的,下篇文章 《MySQL 的可重複讀隔離級別下還存在幻讀的問題嗎?MVCC 機制的實現原理》 分析(立個 flag,這篇文章這週日發)。
- END -本文分享自微信公衆號 - MySQL解決方案工程師(mysqlse)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。