MySQL學習(一)髒讀、不可重複讀、幻讀(鳴人和佐助上學的故事)

髒讀、不可重複讀、幻讀

概念很簡單,網上大都差不多,講的也都很清楚了,就是之前網上看到的實際給的例子很少,感覺理解總是不太透徹,隔斷時間可能記得就不太清楚了,今天想着自己親自實驗下,試下這幾個效果,加深下理解,便於記憶。


1. 髒讀(事務可以讀取未提交的數據)

所謂的髒讀,其實就是讀到了別的事務回滾前的髒數據

比如事務B執行過程中修改了數據X,在未提交前,事務A讀取了X,而事務B卻回滾了,這樣事務A就形成了髒讀。

也就是說,當前事務讀到的數據是別的事務想要修改但是沒有修改成功的數據。

示例:先創建一個空表:

CREATE TABLE `account` (
  `id` int(11) PRIMARY KEY AUTO_INCREMENT,
  `name` varchar(50) DEFAULT NULL comment '賬戶名',
  `balance` decimal(20,2) DEFAULT NULL comment '餘額'
) comment '賬戶表';

旋渦鳴人開學了,初始化一個賬戶,先給自己充值了100塊錢:

INSERT INTO account(`name`, `balance`) values ('旋渦鳴人', 100);

在這裏插入圖片描述
開學了,學校通知每人的賬戶都至少需要 200 元,鳴人的爸爸和鳴人的媽媽同一時間查看鳴人的賬戶裏充值(如果不夠就充值),我們把事務隔離級別設置爲讀未提交看看髒讀是怎麼一回事:

開啓兩個命令行窗口,都設置隔離級別爲讀未提交(read uncommitted):
在這裏插入圖片描述
髒讀:

時間順序 爸爸充值事務 媽媽充值事務
1 開始事務
2 開始事務
3 查詢鳴人賬戶餘額爲 100 元
4 充值 100 元,餘額被更改爲 200 元
5 查詢賬戶餘額爲 200 元(髒讀)
6 充值失敗,事務回滾,餘額變更爲 100 元
7 爸爸看到錢夠了,不再充值了!!
8 提交事務
備註 由於爸爸讀到了未提交的 “髒數據”,導致鳴人去學校發現賬戶錢不夠了。。。

媽媽充值事務:
在這裏插入圖片描述
爸爸充值事務,發生髒讀了:
在這裏插入圖片描述
媽媽充值失敗,事務回滾:
在這裏插入圖片描述
最後鳴人媽媽和爸爸一溝通,都以爲爸爸完成充值了,但鳴人一查賬戶餘額,發現錢不夠,內心…

(解決方案:一個事務在讀取數據的時候,禁止讀取未提交的事務裏數據變更)


2. 不可重複讀(兩次執行同樣的查詢,可能會得到不一樣的結果)

事務A首先讀取了一條數據,然後執行邏輯的時候,事務B將這條數據改變了,然後事務A再次讀取的時候,發現數據不匹配了,就是所謂的不可重複讀了。

也就是說,當前事務先進行了一次數據讀取,然後再次讀取到的數據是別的事務修改成功的數據,導致兩次讀取到的數據不匹配,也就照應了不可重複讀的語義。

示例:

經過上一烏龍事件,吸取教訓,鳴人老爸直接給鳴人賬號充值了 9900 元,現在鳴人有了 10000 元,在學校裏花錢很隨意啦,某一天(還剩5000元),媽媽怕鳴人錢不夠花了,想查下鳴人還有多少錢,這時候爸爸又給鳴人充值 5000 元,於是就發生了不可重複讀事件:

時間順序 爸爸充值事務 媽媽查詢事務
1 開始事務
2 第一次查詢,鳴人賬戶還剩 5000 元
3 開始事務
4 媽媽接了個電話
5 再次充值 5000 元
6 提交事務
7 媽媽想確認一次,第二次查詢,賬戶餘額爲 10000 元
備註 按正確邏輯,媽媽在一次查詢事務裏前後兩次讀取到的數據應該一致

媽媽查詢事務,第一次查詢,賬戶餘額 5000 元:
在這裏插入圖片描述

爸爸充值5000元,提交事務,賬戶餘額更新爲 10000 元:在這裏插入圖片描述

媽媽再次查詢餘額,發現和第一次讀的數據不一致了:
在這裏插入圖片描述

(解決方案:一個事務在讀數據的時候,禁止其他任何事務對該數據進行修改)


3. 幻讀(也是讀取了提交的新事物,指增、刪操作)

高性能mysql第三版對幻讀的解釋是:“所謂幻讀,指的是當某個事務在讀取某個範圍內的記錄時,另外一個事務又在該範圍內插入了新的記錄,當之前的事務再次讀取該範圍的記錄時,會產生幻行”。

幻行怎麼理解? 一開始沒搞明白,看了網上好多說是事務A前後兩次查詢的總條數會因爲中間有事務B新增了一條數據導致查詢詢結果不一致。自己試驗了一下不是這樣子的,兩次查詢條數一模一樣!只是在事務A插入事務B已插入的數據,會提示該行數據已存在,但插入之前明明是沒查到的,這個就是幻影行。

當某個事務在讀取某個範圍內的記錄時,另外一個事務又在該範圍內插入了新的記錄,當之前的事務再次對該範圍的數據進行插入記錄時,會提示數據已存在,即產生了所謂的幻影行

幻讀:當前只有鳴人一個賬戶的數據,假設每個人的賬戶數據都是唯一的。

時間順序 A事務 B事務
1 開始事務
2 第一次查詢,僅有鳴人一個賬戶的數據
3 開始事務
4 第一次查詢,僅有鳴人一個賬戶的數據
5 插入宇智波·佐助賬戶數據
6 提交事務
7 第二次查詢,還是隻有鳴人一個賬戶的數據
8 插入宇智波·佐助的賬戶數據,發現插入失敗!提示佐助賬戶已存在。。。
備註 按正常邏輯,查詢了只有鳴人一個賬戶,再插入佐助的賬戶數據肯定是可以的,但這裏提示重複插入了,這條數據就是所謂的幻影行。 提交當前事務,再次查詢,發現會有倆賬戶數據。

人陸續來了,現在有兩個賬戶了:
在這裏插入圖片描述
開啓兩個會話窗口,設置隔離級別爲可重複讀。

事務A:查詢賬戶總數:
在這裏插入圖片描述

事務B:新增一條數據:
在這裏插入圖片描述

事務 A 第二次查詢總數,發現 id 爲 4 的記錄明明查不到,插入卻發現有些數據已經存在了,之前的檢測獲取的數據如同鬼影一般,即爲幻讀 :
在這裏插入圖片描述

(解決方案:一個事務加上表級鎖,只要有任何東西操作這個表,就上鎖,禁止讀寫併發)

But,因爲表級鎖性能太低下了,InnoDB 存儲引擎並沒採取這種方案,而是通過 間隙鎖 + 多版本併發控制(MVCC -> Multiversion Concurrency Control)解決了幻讀的問題。

不可重複讀的重點是修改:同樣的條件, 你讀取過的數據, 再次讀取出來發現值不一樣了;
幻讀的重點在於 事務A第一次讀取數據,事務B對同一個表進行了insert,事務A第二次讀取數據沒讀到,重新插入一次,提示已存在!這時是幻讀。

不可重複讀重點在於update,而幻讀的重點在於insert和delete。

想要把髒讀、不可重複讀、幻讀弄的更清楚之前,需要先學習一波事務,在事務不同的隔離級別下會產生不同的問題,上面簡單介紹了這幾個xx讀的概念,只需要瞭解概念的童靴掃描一下看看概念就可以啦。如果想了解更深入一點,繼續往下看吧!
ps:本來事務也想詳細寫寫的,後來發現寫完上邊幾個概念就佔用了好大一塊,太長啦,事務就先簡單羅列了下概念,後面單獨再寫個~

最常見、常問的是 RC(讀已提交)、RR(可重複讀),一句話概括他倆的區別:RC讀的是當前存在的最新版本的數據,RR讀的是事務開啓之前的最後一個版本狀態的數據。

在這裏插入圖片描述

事務

事務是什麼

事務就是一組原子性的 SQL 查詢,或者說一個獨立的工作單元。如果數據庫引擎能夠成功地對數據庫應用該組查詢的全部語句,那麼就執行該組查詢。如果其中有任何一條語句因爲崩潰或其他原因無法執行,那麼所有的語句都不會執行。通俗的說,事務內的語句,要麼全部執行成功,要麼全部執行失敗。

事務的 4 種特性 - ACID

原子性、隔離性、持久性是手段,一致性是目的。

原子性(atomicity)

事務是一個完整的操作。事務的各步操作是不可分的(原子的);要麼都執行,要麼都不執行。

一致性(consistency)

當事務完成時,數據必須處於一致狀態。

隔離性(isolation)

對數據進行修改的所有併發事務是彼此隔離的,這表明事務必須是獨立的,它不應以任何方式依賴於或影響其他事務。

持久性(durability)

事務完成後,它對數據庫的修改被永久保持,事務日誌能夠保持事務的永久性。

事務的 4 種隔離級別(強弱級別逐漸遞增)

隔離級別越弱,出現的問題越多!打個比方,如果騰訊文檔同一時間允許多人同時讀取、同時修改,和同一時間只允許一個人讀取、修改,哪個會出現更多問題呢 ?

讀未提交(read uncommitted)

在一個事務中,可以讀取到其他事務未提交的數據變化。

讀已提交(read committed)

在一個事務中,可以讀取到其他事務已經提交的數據變化。

可重複讀(repeatable read)

可重複讀是 MySQL 的默認事務隔離級別。

在一個事務中,直到事務結束前,都可以反覆讀取到事務剛開始時看到的數據,並且一直不會發生變化。

可串行化(serializable)

最高的隔離級別,所有的事務串行執行,資源消耗最大。

簡單來說,這個級別會在讀取的每一行數據都加鎖,所以可能導致大量的超時和鎖爭用的問題。

MySQL 查看及設置事務隔離級別

  1. 查看:select @@tx_isolation
  2. 設置:set session transaction isolation level 事務隔離級別

MVCC

MVCC 是行級鎖的一個變種,但是它在很多情況下避免了加鎖操作,因此開銷更低。

MVCC 的實現,是通過保存數據在某個時間點的快照來實現的。也就是說,不管需要執行多長時間,每個事務看到的數據都是一致的。根據事務開始的時間不同,每個事務對同一張表、同一時刻看到的數據可能是不一樣的。

描述如有錯誤不足之處,各位看官請指正下哈~

參考資源:

《高性能MySQL》
https://www.cnblogs.com/wangenxian/p/11014504.html (概念解釋:髒讀、不可重複讀、幻讀)
https://blog.csdn.net/xmh594603296/article/details/79676844
https://blog.csdn.net/qq_33591903/article/details/81672260 (快速理解髒讀、不可重複讀、幻讀概念)髒讀舉例裏我這邊驗證了感覺有一點點小問題,我試了在產生了髒讀之後,再直接更新餘額,會更新失敗。。
上面這個鏈接,我感覺幻讀解釋的也不對呢。。。
自己實驗後,發現和下邊這個鏈接裏幻讀的含義是一致的:
https://www.cnblogs.com/xiaohanlin/p/8644749.html
https://baijiahao.baidu.com/s?id=1659659238415878074&wfr=spider&for=pc (幻讀)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章