Mysql是如何實現隔離性的?(鎖+MVCC)
隔離性是指一個事務內部的操作以及操作的數據對正在進行的其他事務是隔離的,并發執行的各個事務之間不能相互干擾。隔離性可以防止多個事務并發執行時,可能存在交叉執行導致數據的不一致。
MySQL 對隔離性的保證主要有兩個方面:
--用鎖機制來保證一個事務寫操作對另一個事務寫操作的隔離性,
---用 MVCC 機制來保證一個事務寫操作對另一個事務讀操作的隔離性。
MySQL 中按照鎖的粒度,可以分為全局鎖,表鎖和行鎖。
全局鎖會使整個數據庫處于只讀狀態,在做全庫邏輯備份時經常用到。表級鎖在操作數據時會鎖定整張表,并發性能一般,而行鎖可以做到只鎖定需要操作的記錄行,并發性能很好。但是由于加鎖本身需要消耗資源,因此某些在鎖定數據較多的情況下可以使用表鎖來減少開銷。
MVCC 主要解決的是讀寫沖突的問題,它是由 undo log + read view 實現的。其中 undo log 可以為每條記錄保存多個歷史版本,MySQL 在執行快照讀的時候,會根據事務的 read view 里的信息,順著 undo log 的版本鏈找到滿足可見性的記錄。
MVCC-Multi-Version Concurrency Control 多版本并發控制
是指維護一個數據的多個版本,使得讀寫操作沒有沖突,快照讀為MySQL實現MVCC提供了一個非阻塞讀功能。MVCC的具體實現,還需要依賴于數據庫記錄中的三個隱藏字段、undo log日志、readView。
數據庫記錄中的三個(兩個)隱藏字段:
?undo log日志:
多個事務 提交事務之后undo log不會立即刪除.原因是有活動的事務,正在用到這個undo log,結合ReadView..
undo log版本鏈:?
- 在不同事務/相同事務對同一記錄進行修改,修改過程中會記錄 該記錄的undo log日志。
- 會導致該記錄的uodo log生成一條記錄版本鏈表。鏈表的頭部是最新的舊記錄,尾部是最早的舊記錄!
- undo log日志中記錄了當前記錄(行數據)的歷史版本。
查詢時應該返回哪一個版本呢??
具體返回哪個版本,要取決于MVCC實現原理中的readview
ReadView(讀視圖)
ReadView 是 快照讀 SQL執行時MVCC提取數據的依據,記錄并維護系統當前活躍的事務(未提交的)id。
?版本鏈數據訪問規則:
?不同的隔離級別,生成ReadView的時機不同:
- RC: 在事務每次快照讀的時候,都會產生一個新的ReadView....(里面維護著當前活動的事務ID)
- RR: 在事務第一次快照讀的時候,會產生一個新的ReadView,后續的每次快照讀都會復用第一個ReadView..(里面維護著當前活動的事務ID)(這是造成 不可重復讀 和 幻讀的本質原因)
RC隔離級別下:
每次快照讀的時候,會從當前記錄的最新版本開始,和ReadView中的字段,通過版本鏈數據訪問規則開始對比。如果符合就返回當前 版本記錄,如果不符合,就按照版本鏈的鏈表往下繼續做對比,直到找到符合規則的那個版本返回。。
因為RC(讀已提交)隔離級別下每次快照讀都是新的ReadView,所以維護的活動事務ID集合m_ids都可能不一樣,所以會有不可重復讀的現象出現!(問題根源)
比如上圖:一次快照讀是{3,4,5},一次快照讀是{4,5}
RR隔離級別下:
第一次快照讀,會產生一個ReadView,分別記錄四個屬性,接下來第二個快照讀的時候,不會再生成一個ReadView了 ,直接復用第一個,所以匹配規則也都 一樣,在undo log版本鏈中查找時查出來的數據 也都一樣,這就保證了 可重復讀!
所以:MVCC--?它的主要作用就是在 快照讀 的時候,決定我們提取的到底是哪一個版本
總結:
擴展:
1、當前讀
當前讀就是讀取到的是最新的數據!通過select .. lock in share mode(共享鎖)實現,或通過select ...for update、update、insert、delete(排它鎖)等實現!
?2、快照讀
3、在InnoDB存儲引擎下的RR隔離級別下:
- 快照讀會使用MVCC. (select * ... 普通sql語句)
- 當前讀會采用Next-Key-Lock,是行鎖 + 間隙鎖的方式來處理(鎖機制). ?(select ..lock in share mode/for ..update ?update ?insert delete)??
4、 RR級別下 快照讀 使用了MVCC一定能避免幻讀嗎?
- 能,但不完全能!
- 因為MVCC并不是采用鎖的方式完全的對事務數據進行了隔離,而是通過版本控制變相的實現了解決幻讀的功能
特例:當兩次快照讀之間存在當前讀,ReadView會重新生成,會導致產生幻讀。
?MVCC在快照讀的情況下可以解決幻讀問題,但是在當前讀的情況下是不能解決幻讀的
5、RR可重復讀隔離下為什么會產生幻讀?
在可重復讀隔離級別下,普通的查詢是快照讀,是不會看到別的事務插入的數據的(因為復用同一個 ReadView)。因此,幻讀在?當前讀?下才會出現。
SELECT * FROM player LOCK IN SHARE MODE; SELECT * FROM player FOR UPDATE; INSERT INTO player values ... DELETE FROM player WHERE ... UPDATE player SET ...
6、什么是幻讀
在可重復讀的數據隔離級別下,在同一個事務中,使用當前讀,讀到了其他事務新插入的數據的現象叫做幻讀。這里有幾個定語:可重復讀的事務隔離級別,當前讀,插入的新數據。只有在這些定語的約束下?才能形成幻讀。?
我們知道可重復讀的數據隔離級別下,一個事務無法查詢到另外一個事務對數據表進行的變更,不過,這個結論的前提是,這個查詢是一個普通查詢,也就是快照讀,如果查詢的方式是當前讀(select * from table for update),那么就可以查詢到另外一個事務的變更結果的。但是"幻讀"的定義,對"變更"又做了更嚴格的限制,幻讀,只僅僅針對 "insert" 的變更,而對 "update" 的變更,雖然查詢也可以感知到,但這不會被稱為幻讀。雖然這里說的有點啰嗦,但是幻讀的定義就是那么嚴格,所以我要多強調一遍。
我們在使用MVCC的時候,一般是根據業務場景選擇組合搭配,樂觀鎖或悲觀鎖。
MVCC用來解決讀寫沖突問題,樂觀鎖或者悲觀鎖用來解決寫和寫的沖突。從而最大程度的提高數據庫的并發性?。