1. 程式人生 > >mysql可重複讀和幻讀例項

mysql可重複讀和幻讀例項

mysql的預設事務級別是:可重複讀
其中可重複讀是通過mvcc來實現的又叫快照讀,在事務中的讀操作通過對當前的資料庫中記錄一個版本,以後的讀操作只會讀取記錄的版本,因此相當於對資料庫的資料建立了一個快照資料,因此叫做快照讀,其不用對資料庫中的資料進行加鎖又叫做樂觀鎖。
同時RR事務級別的mysql通當前讀和gap鎖來解決幻讀,其本質是通過對資料庫周邊記錄進行加悲觀鎖(讀鎖(共享鎖)和互斥鎖(寫鎖))【gap鎖】來解決幻讀。

1.什麼是幻讀問題

RR事務隔離級別號稱可以解決幻讀的問題(通過當前讀加鎖來實現)
第一步建表並插入5條記錄:
這裡寫圖片描述

接下來我們看下大部分mysql所說的幻讀現象:
事務1(開啟事務查詢發現沒有記錄6準備插入):
這裡寫圖片描述


事務2(開啟事務,發現沒有記錄6插入,並提交事務):
這裡寫圖片描述
事務1:查詢發現沒有記錄6,現在開始進行插入6:
這裡寫圖片描述
沒有的記錄我要插入卻告訴已經存在,這就是通常說的幻讀。

2.gap鎖(當前讀)解決幻讀問題

mysql說對資料加鎖不管共享鎖還是互斥鎖就能解決幻讀的問題
開啟一個事務1(加上共享鎖解決幻讀的情況):
這裡寫圖片描述
開啟事務2(查詢發現沒有記錄8,準備插入)
這裡寫圖片描述
可以發現事務2被阻塞,不準插入,除非事務1提交。因此在事務1中插入記錄8是能夠成功的。
事務1插入記錄8,最後可以查詢出自己插入的資料,但是更新一條不存在的資料是不會查詢出來的,最後提交事務:
這裡寫圖片描述
事務2阻塞的操作會爆重複異常:
這裡寫圖片描述


因此對一個事務加上悲觀鎖(共享鎖或者互斥鎖)是能夠保證幻讀不會出現的,並且誰先加鎖,誰就能夠保證check and insert是成功的。如果你需要每個事務的check and insert都能成功,那麼你不要加共享鎖,直接加互斥鎖。那麼事務會直接阻塞在加鎖階段,就不會出現check and insert 失敗的情況。
事務1加上互斥鎖
這裡寫圖片描述
事務2也想加互斥鎖,BOOM你只能失敗:
這裡寫圖片描述

3.RR沒有解決的幻讀

場景:我們知道grap鎖能夠將右邊的記錄進行加鎖,因此我要統計表記錄的數量,我只需要對最大記錄加鎖就行了
事務1:對最大記錄互斥加鎖,準備計算表中記錄數量。發現數量為8,並且不存在3和4的記錄。
這裡寫圖片描述


事務2:插入缺失記錄3和4成功,但是插入id為100的記錄被鎖住,OK事務2可以提交了。
這裡寫圖片描述
事務1:在事務1在事務2插入資料後進行查詢總量,發現數量還是6,OK很完美
這裡寫圖片描述
事務1:接下來我發現事務1中沒有3和4這條記錄,我進行一次無用的更新會發生什麼?
這裡寫圖片描述
OK,最終問題終於出現了,為什麼我更新兩條不存在的記錄,我能夠更新成功,並且我再次統計的時候,數量添加了兩條?並且我重新讀取,發現結果不一樣了,不是說可以重複讀嗎?加鎖可以解決幻讀嗎?
PS:如果一個事務裡面插入或者刪除會改變記錄的資料,這個是合理的不是幻讀。更新應該只會修改資料記錄,這個也是合理,同樣不是幻讀,都是可重複讀的。但是更新出現新的記錄就是一種異常的情況。