Mysql 百問系列:幻讀究竟是什麼?

問題:

1.什麼樣的狀況叫作幻讀?
2.Mysql 可重複讀隔離級別下,到底能不能阻止幻讀?
3.什麼是當前讀,什麼是快照讀?sql

幻讀的定義:

事務A 按照必定條件進行數據讀取, 期間事務B 插入了相同搜索條件的新數據,事務A再次按照原先條件進行讀取時,發現了事務B 新插入的數據 稱爲幻讀併發

若是事務A 按必定條件搜索, 期間事務B 刪除了符合條件的某一條數據,致使事務A 再次讀取時數據少了一條。這種狀況歸爲 不可重複讀ui

準備工做:

Mysql 隔離級別爲 可重複讀spa

CREATE TABLE `author` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) DEFAULT NULL,
  `age` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8;

INSERT into author VALUES (1,'g1',20),(5,'g5',20),(15,'g15',30),(20,'g20',30);
複製代碼

查找年齡爲20歲的做者,並把姓名改爲G0

時間 Session 1 Session2
begin;
T1 select * from author where age = 20;
結果: (1,g1,20),(5,g5,20)
T2 INSERT into author VALUES (25,'g25',20);
T3 select * from author where age = 20;
結果: (1,g1,20),(5,g5,20)
T4 update author set name = 'G0' where age = 20
結果: 顯示影響行數爲3行。
T5 select * from author where age = 20;
結果:(1,g0,20),(5,g0,20),(25,g0,20)

來分析下情形:code

  1. T1時刻 讀取年齡爲20的數據, Session1拿到了2條記錄。
  2. T2時刻 另外一個進程Session2插入了一條新的記錄,年齡也爲20
  3. T3時刻,Session1再次讀取年齡爲20的數據,發現仍是2條數據,貌似 Session2新插入的數據並未影響到Session1的事務讀取。

對於T1 -- T3 時刻的情形,從結果來看,在可重複度讀隔離級別下彷佛解決了幻讀的問題。進程

  1. T4時刻,Session1 修改年齡爲20的數據, 發現影響行數爲3條。 爲何T3時候只能查到2條數據,但如今修改卻修改了3條數據?
  2. T5時刻,Session1 再次讀取年齡爲20的數據,發現結果變成了3條,咱們知道被修改的第三條就是Session2在T2時刻新增的一條。

T4,T5 的結果來看,Session1 讀到了 Session2 新插入的數據。產生了幻讀現象事務

到底可重複讀隔離級別下,解決了幻讀問題沒有?

瞭解過MVCC的同窗,確定知道或據說過當前讀,和快照讀。(不知道的同窗,能夠查找相關資料瞭解下,固然後續我也會有文章專門介紹MVCC)。
首先要知道的是MVCC 就InnoDB 秒級創建數據快照的能力。 快照讀就是讀取數據的時候會根據必定規則讀取事務可見版本的數據。 而當前讀就是讀取最新版本的數據。
什麼狀況下使用的是快照讀:(快照讀,不會加鎖)string

通常的 select * from .... where  ...  語句都是快照讀it

什麼狀況下使用的是當前讀:(當前讀,會在搜索的時候加鎖)io

select * from .... where  ... for update select * from .... where  ... lock in share mode update .... set .. where ... delete from. . where ..

若是事務中都使用快照讀,那麼就不會產生幻讀現象,可是快照讀和當前讀混用就會產生幻讀。

若是都是使用當前讀,能不能解決幻讀問題?

先讓咱們數據恢復到初始狀態

TRUNCATE TABLE author;
INSERT into author VALUES (1,'g1',20),(5,'g5',20),(15,'g15',30),(20,'g20',30);
複製代碼
時間 Session 1 Session2
begin;
T1 select * from author where age = 20 for update;
結果: (1,g1,20),(5,g5,20)
T2 INSERT into author VALUES (25,'g25',20);
阻塞,等待鎖
T3 select * from author where age = 20 for update;
結果: (1,g1,20),(5,g5,20)

能夠看到Session 2 被阻塞了。須要等到Session1 提交事務後才能完成。當咱們在事務中每次讀取都使用當前讀,也就是人工把InnoDB變成了串行化。必定程度上下降了併發性,可是也一樣避免了幻讀的狀況。
當前讀爲何會阻塞新數據的插入,主要是間隙鎖的加鎖機制。(Mysql 的也是個大問題,後續有專門的章節介紹)

總結:

  • 但願經過這篇文章,瞭解幻讀的定義。讀到了其餘事務新插入的數據,這種現象叫幻讀。
  • 當前讀和快照讀的區別,以及瞭解何時是快照讀,何時是當前讀
  • 可重複讀隔離級別下,一個事務中只使用當前讀,或者只使用快照讀都能避免幻讀。
相關文章
相關標籤/搜索