MySQL InnoDB引擎在Repeatable Read(可重復讀)隔離級別下,到底有沒有解決幻讀的問題?
網上眾說紛紜,有的說解決了,有的說沒解決,甚至有些大v的意見都無法達成統一,
今天就深入剖析一下,徹底解決這個幻讀的問題,
解決幻讀問題之前,先普及幾個知識點,
1. 并發事務產生的問題
先創建一張用戶表,用作資料驗證:
CREATE TABLE `user` (
`id` int NOT NULL AUTO_INCREMENT COMMENT '主鍵',
`name` varchar(100) DEFAULT NULL COMMENT '姓名',
PRIMARY KEY (`id`)
) ENGINE=InnoDB COMMENT='用戶表';
并發事務會產生下面三個問題:
臟讀
定義: 一個事務讀到其他事務未提交的資料,

從上面的示例圖中,可以看出,在事務2修改完資料,沒有提交的情況,事務1已經讀到事務2最新修改的資料,這種情況就屬于臟讀,
不可重復讀
定義: 一個事務讀取到其他事務修改過的資料,

從上面的示例圖中,可以看出,在事務2修改完資料,并提交事務后,事務1第二次查詢已經讀到事務2最新修改的資料,這種情況就屬于不可重復讀,
幻讀
定義: 一個事務讀取到其他事務最新插入的資料,

從上面的示例圖中,可以看出,在事務2插入完資料,并提交事務后,事務1第二次查詢已經讀到事務2最新插入的資料,這種情況就屬于幻讀,
2. 快照讀和當前讀
再普及一下快照讀和當前讀,
快照讀: 讀取資料的歷史版本,不對資料加鎖,
例如:select
當前讀: 讀取資料的最新版本,并對資料進行加鎖,
例如:insert、update、delete、select for update、select lock in share mode,
3. 再談幻讀問題
MySQL在Repeatable Read(可重復讀)隔離級別下,到底有沒有解決幻讀的問題?
只能說是部分解決了幻讀問題,
首先,在快照讀的情況下,是通過MVCC(復用讀視圖)解決了幻讀問題,
想詳細了解MVCC和讀視圖,可以翻一下上篇文章,
先手動設定一下MySQL的隔離級別為可重復讀:
SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;

執行測驗用例,驗證一下:

從上面的示例圖中,可以看出,事務1的兩次查詢,得到的結果一致,并沒有查到事務2最新插入的資料,
原因是,在可重復讀隔離級別下,第一次快照讀的時候,生成了一個讀視圖,第二次快照讀的時候,復用了第一次生成的讀視圖,所以兩次查詢得到的結果一致,
所以,在快照讀的情況下,可重復讀隔離級別是解決了幻讀的問題,
再測驗一下,在當前讀的情況下,可重復讀隔離級別是否解決幻讀問題:

從上面的示例圖中,可以看出,事務1的兩次查詢,得到的結果不一致,在事務2插入資料,并提交事務后,事務1的第二次執行當前讀(加了for update)的時候,讀到了事務2最新插入的資料,
原因是,在可重復讀隔離級別下,每次執行當前讀會生成一個新的讀視圖,所以能讀到其他事務最新插入的資料,
所以,在當前讀的情況下,可重復讀隔離級別是沒有解決了幻讀的問題,
在執行上面的測驗用例的時候,我忽然想到一個問題,既然select for update的當前讀,出現了幻讀問題,是不是其他的當前讀也會復現幻讀問題,比如insert,
再執行測驗用例,驗證一下:

跟預想的一樣,在insert當前讀的情況下,也出現了幻讀的問題(主鍵沖突),
那有沒有什么辦法?在可重復讀隔離級別下,執行當前讀的時候,也能解決幻讀的問題?
當然有的,唯一的辦法就是加鎖,

事務1在執行第一次查詢的時候,就對資料進行加鎖(使用for update),防止其他事務修改資料,這樣也就徹底解決了幻讀問題,
你覺得有什么好辦法嗎?

轉載請註明出處,本文鏈接:https://www.uj5u.com/shujuku/507264.html
標籤:其他
