MySQL MVCC机制解析
1、MVCC(multi version concurrent control)機制
多版本并發控制機制,專門控制多個事物并發運行的時候,互相之間會如何影響。
MVCC機制 = undo log多本本鏈條+ReadView機制
1)ReadView:執行一個事務,會生成一個ReadView
關鍵的四個字段
- m_ids:有哪些事務在MySQL里執行還沒提交
- min_trx_id:m_ids里最小的值
- max_trx_id:mysql下一個要生成的事務id,最大的事務id
- creator_trx_id:現在事務的id
假設原來數據庫有一行數據,事務id是32,他的值就是初始值,如下圖所示。
?此時有兩個事務并發執行,事務A(id=45),事務B(id=59),事務B要更新這行數據的值,事務A要讀取這行數據的值。
現在事務A直接開啟一個ReadView,這個ReadView里的m_ids就包含了事務A和事務B的兩個id,45和59,所以min_trx_id就是45,max_trx_id就是60,creator_trx_id是45,就是事務A本身。
事務A第一次查詢這行數據,會判斷當前這行數據的trx_id是否小于ReadView中的min_trx_id,此時發現txr_id是32,小于ReadView的min_trx_id,也就是45,說明事務開啟之前,修改這行數據的事務早就提交了,此時可以查到這行數據,如下圖所示:
事務B開始執行,將這行數據改為值B,txr_id設置為自己的id,59,同時roll_pointer指向修改之前生成的undo log,如下圖所示:?
?這個時候事務A再次查詢,此時數據行里txr_id=59,那么這個txr_id大于ReadView里的min_txr_id(45),小于ReadView中的max_trx_id(60),說明更新這條數據的事務,是跟自己差不多同時開啟的,不可以查這條數據。
順著這條數據的roll pointer順著undo log日志鏈條往下找,就會找到最近的一條undo log , trx id是32,此時發現trx id=32,是小于ReadView里的min trx id (45 )的,說明這個undo log版本必然是在事務A開啟之前就扶行且提交的。查詢最近的那個undo log里的值好了,這就是undo log多版本鏈條的作用,他可以保存一個快照鏈條,讓你可以讀到之前的快照值。
通過這套ReadView + undo log 日志鏈條的機制可以保證事務A不會讀到并發執行的事務B更新呢的值,只會讀到之前的值。
此時事務A自己更新這行數據,改成值A,trx_id修改為45。
事務A 再次來查詢這條數據的值,發現trx_id,與自己ReadView中的creator_trx_id是一樣的,說明這行數據就是自己修改的,因此可以看到這條數據。如下圖:
此時事務A執行的過程中,突然開啟了事務C,這個事務的id是78,然后他更新了哪行數據的值為值C?,并且提交,如下圖:
此時事務A再去查詢,會發現當前數據的trx_id=78,大于自己ReadView中的max_trx_id(60),說明這條數據是事務A開啟后,新的事務來更新了數據,因此看不到這條數據。如下圖:
?事務A會順這undo log鏈條往下找,先找到值A自己修改過的那個版本,因為trx_id=45與自己ReadView中的Creator_trx_id是一樣的,所以讀取trx這條數據。如下圖:
?通過undo log+ReadView機制,保證你只能讀到你事務開啟之前的數據,別的提交事務更新的值,還有就是你自己事務更新的值。可以實現多個事務并發執行時候的數據隔離。
2、RC(Read Committed)基于ReadVIew的實現
核心:一個事務設置處于RC隔離級別的時候,他每次發起查詢,都重新生成一個ReadView。
本質:協調多個事務并發運行的時候,并發的讀寫同一批數據,協調互相的可見性。
解析:
此時數據庫有一條事務id=50的數據,另外活躍的兩條事務,事務A(id=60),事務B(id=70)。如下圖:
當事務B發起一次update操作,更新了這條數據,把這條數據的值修改為了值B,此時數據的trx_id變為事務B的id=70,同時生成一條undo log,由roll_pointer指向trx_id=50的數據。如下圖:
?此刻事務A發起查詢,生成一個ReadView,此時ReadView里的min_trx_id=60,max_trx_id=71,creator_trx_id=60,如下圖所示:
?事務A發現當前這條數據的trx_id=70,屬于ReadView的事務id范圍之內,說明是他生成ReadView之前就有這個活躍的事務B,事務B修改了這條數據的值,還沒有提交,所以ReadView的m_id活躍事務列表里,有[60,70]兩個id,所以此時根據ReadView的機制,此時事務A無法查到事務B修改的值B。
順著鏈條下找,發現trx_id=50的數據,小于當前ReadView中的min_trx_id=60,說明這條數據是事務A生成ReadView之前插入的并且提交了,因此可以查到,如下圖所示:
此時事務B提交后,事務A再次發起查詢,再次生成一個ReadView,現在活躍的事務只有事務A,所以min_trx_id=60,max_trx_id=71,m_ids活躍事務列表中只會有一個60,,如下圖:
事務A再次基于ReadView查詢,發現這條數據的trx_id?=70,雖然在ReadView的min_trx_id和max_trx_id范圍之間,但是并不在m_ids列表中,說明事務B在事務A生成ReadView之前就提交了,此時事務A可以查詢到事務B。如下圖所示:
3、RR(Repeatable Read)基于ReadView的實現
RR級別下,事務讀一條數據,無論讀多少次,都是一個值,別的事務修改數據之后哪怕提交了,你也看不到人家修改的值,避免了不可重復讀的問題;如果別的事務插入一些新數據,你也是讀不到的,避免了幻讀的問題。
1)、解決不可重復讀
數據庫有一條事務id=50的數據,此時有兩條事務同時運行,事務A(id=60),事務B(id=70)。如下圖所示
?事務A發起查詢并生成一個ReadView,此時ReadView里的creator_trx_id=60,min_trx_id=60,max_trx_id是71,m_ids是[60,70]。如下圖所示:
事務A基于自身的ReadView去查詢這條數據,會發現這條數據的trx_id為50,是小于ReadView里的min_trx_id,可以查到這條原始值
事務B此時更新了這條數據的值為值B,此時會修改trx_id為70,同時生成一個undo log,并且提交事務。如下圖所示:
ReadView一旦生成就不會改變,雖然事務B結束了,但是事務A的ReadView中m_ids還是會有[60,70]兩個事務id。
此時事務A去查詢這條數據,會發現此時的數據是trx_id=70,還在m_ids列表中,事務A是不能查詢到書屋B更新的這個值,因此會繼續順著指針去鏈表中找。如下圖所示:
事務找到trx_id=50這條數據,小于本身ReadView中的min_trx_id(60),此時事務A可以查詢到這條數據。如圖示:
?此刻避免了不可重復讀
2)、解決幻讀
事務A使用“select * from table where id > 10”來查詢,查到trx_id=50的數據(查不到trx_id=70數據的原因不再做描述,見上方解析)如下圖:
?事務C插入一條數據并且提交
?此時事務A再次查詢,發現trx_id=50,trx_id=80都兩條數據,但是trx_id=80大于自己ReadView中max_trx_id(71),說明此條數據是事務A執行后才開始執行的,不能查詢。如下圖所示:
?因此事務A根本不會發生幻讀。
總結
以上是生活随笔為你收集整理的MySQL MVCC机制解析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: OOO Execution May No
- 下一篇: java中的输入输出