MVCC 英文全稱爲Multi-Version Concurrency Control,翻譯爲中文即 多版本併發控制。實現非堵塞式併發訪問。這個原理有點類似《Java裏面的CAS實現原理》誰像誰說不定類比來學吧
- 事務隔離級別
髒寫:一個事務修改了另一個未提交事務修改過的數據,也就是區修改別人未提交的部分當回滾時就修改不復存在了。
髒讀:一個事務讀到了另一個未提交事務修改過的數據,同樣的回滾時就讀取到了一個不存在的數據,
不可重複讀:就是說一個事務沒有提交的時候讀取了另一個數據,讀取之後這個數據被另外一個事務給修改了,當這個未提交的事務再次讀取該數據時和之前的不一樣了。這就發生了不可重複讀,就是在同一事務讀取多次的值不一樣。
幻讀:和不可重複讀類似,不過是兩次讀取的記錄數量不一樣,像幻影般突然的出現。
SQL標準的隔離級別如下:
隔離級別 | 名稱 | 髒讀 | 不可重複讀 | 幻讀 |
---|---|---|---|---|
READ UNCOMMITTED | 未提交讀 | Possible | Possible | Possible |
READ COMMITTED | 已提交讀 | Not Possible | Possible | Possible |
REPEATABLE READ | 可重複讀 | Not Possible | Not Possible | Possible |
SERIALIZABLE | 可串行化 | Not Possible | Not Possible | Not Possible |
不出現髒寫是因爲,所有隔離級別都不允許出現髒寫。
- MySQL的隔離級別
不同的數據庫支持標準SQL的隔離級別是不一樣的,MySQL四種隔離級別都支持,且MySQL的默認隔離級別爲REPEATABLE READ,我們可以手動修改一下事務的隔離級別。而且MySQL在REPEATABLE READ隔離級別下,是可以禁止幻讀問題的發生的。
1、使用READ UNCOMMITTED隔離級別的事務,由於可以讀到未提交事務修改過的記錄,所以直接讀取記錄的最新版本就好了
2、對於使用SERIALIZABLE隔離級別的事務來說,設計者規定使用加鎖的方式來訪問記錄
3、對於使用READ COMMITTED和REPEATABLE READ隔離級別的事務來說,都必須保證讀到已經提交了的事務修改過的記錄,也就是說假如另一個事務已經修改了記錄但是尚未提交,是不能直接讀取最新版本的記錄的,核心問題就是:需要判斷一下版本鏈中的哪個版本是當前事務可見的, - readview
readview保存當前活躍的讀寫事務ID、當前活躍的最小事務ID、下一個即將生成的事務ID(通過生成事務的那個全局變量預測)、生成該readview的事務ID。有了這個readview,當要訪問一個記錄時先判斷該記錄的隱藏列中有一個事務ID,即修改該記錄的事務ID,如果小於且不在活躍readview裏的活躍列表,說明事務已經提交了就可以直接訪問,如果還在活躍列表裏就說明還在活躍不可以讀取否則就髒讀。如果大於就說明生成readview後才生成的事務是不可訪問否則會出現不可重複讀或幻讀。當不可訪問時即會獲取隱藏列裏的回滾指針找到undo日誌然後繼續對比直到可以訪問或者版本鏈的結尾爲止。《版本鏈是啥》 - RC和RR隔離級別的不同實現
READ COMMITTED —— 每次讀取數據前都生成一個ReadView
也就是說事務裏面有多次讀取操作的話,每次讀取都會生成一個ReadView快照,也就是說上一次讀取的時的ReadView和本次讀取的可能不一樣,所以可能會讀到不一樣的版本也就是不可重複讀或幻讀
REPEATABLE READ —— 在第一次讀取數據時生成一個ReadView
也就是說事務一旦開始,第一次查詢和同一個事務中的第幾次查詢都是同一個ReadView,也就是版本定格快照在開啓事務那個刻,以後每次讀取到的版本都一樣,也即是不會發生不可重複讀或幻讀
髒讀都不可能發生,因爲這兩種讀取都在事務活躍的版本不可讀的,也就是隻能讀取到已經提交的事務的版本。所以不可能髒讀。
這個原理有點類似《Java裏面的CAS實現原理》誰像誰說不定類比來學吧
- 思考
會發現MVCC好啊,不會堵塞,但是發現只是解決了讀的問題,寫入沒法保證,怎麼避免不會發生髒寫呢,當然是通過鎖的方式來了,修改一個記錄前先鎖住不給其他事務修改也就是不會發生髒寫了。還有一個問題,當某個業務需要每次都要讀到最新的值咋辦這個MVCC讀到的可能是舊版本,這需求當然也是通過鎖來搞定了讀寫都鎖就對了。還有就是明白一個是,常規的查詢語氣中不會加鎖,除非顯式的寫加鎖,常規不做其他聲明的寫語氣中會加鎖保證不會發生髒鎖,,,,鎖的詳細內容
轉至《MySQL——鎖機制簡述》