MySQL / 可重复读到底是怎么实现的?图解 ReadView 机制
一、回顧一下 undo log 回滾日志
在講可重復(fù)讀的底層原理之前,我們有必要看一下之前畫的圖,重新回顧一下 undo log 回滾日志。
當(dāng) MySQL 執(zhí)行寫操作之前,會(huì)把即將被修改的數(shù)據(jù)記錄到 undo log 日志里面。
只有這樣,事務(wù)要回滾的時(shí)候,即使 Buffer Pool 中的數(shù)據(jù)被修改了,依然可以從 undo log 日志中,讀取到原插入、修改、刪除之前的值,最終把值重新變回去,這就是回滾操作。
二、undo log 版本鏈
undo log 版本鏈?zhǔn)腔?undo log 實(shí)現(xiàn)的。undo log 中主要保存了數(shù)據(jù)的基本信息,比如說日志開始的位置、結(jié)束的位置,主鍵的長度、表 id,日志編號(hào)、日志類型。
此外,undo log 還包含兩個(gè)隱藏字段 trx_id 和 roll_pointer。trx_id 表示當(dāng)前這個(gè)事務(wù)的 id,MySQL 會(huì)為每個(gè)事務(wù)分配一個(gè) id,這個(gè) id 是遞增的。roll_pointer 是一個(gè)指針,指向這個(gè)事務(wù)之前的 undo log。
如下圖所示,現(xiàn)在有一個(gè) id 為 10 的事務(wù) A 正在執(zhí)行,undo log 日志的信息如下所示:
緊接著 id 為 18 的事務(wù) B 開始執(zhí)行,就會(huì)再生成一條 undo log 日志,同時(shí)新生成的日志的 roll_pointer 指向上一條 undo log 日志。
日志與日志之間通過 roll_pointer 指針連接,就形成了 undo log 版本鏈。
三、基于 undo log 版本鏈實(shí)現(xiàn)的 ReadView 機(jī)制
鋪墊了這么多,到這里終于可以說說什么是 Readiew 了。
ReadView 說白了就是一種數(shù)據(jù)結(jié)構(gòu),它主要包含這樣幾部分:
- m_ids,當(dāng)前有哪些事務(wù)正在執(zhí)行,且還沒有提交,這些事務(wù)的 id 就會(huì)存在這里;
- min_trx_id,是指 m_ids 里最小的值;
- max_trx_id,是指下一個(gè)要生成的事務(wù) id。下一個(gè)要生成的事務(wù) id 肯定比現(xiàn)在所有事務(wù)的 id 都大;
- creator_trx_id,每開啟一個(gè)事務(wù)都會(huì)生成一個(gè) ReadView,而 creator_trx_id 就是這個(gè)開啟的事務(wù)的 id。
為了能把這些概念說清楚,接下來我會(huì)畫幾張圖幫助大家理解,不明白的可以多看幾遍圖!
先來看第一張圖。
事務(wù)是可以并發(fā)執(zhí)行的,現(xiàn)在有事務(wù) A、事務(wù) B 這兩個(gè)事務(wù),且這兩個(gè)都沒有提交。事務(wù) A 將會(huì)執(zhí)行多次讀操作,來模擬可重復(fù)讀中多次讀取同一行數(shù)據(jù)的場(chǎng)景。事務(wù) B 則會(huì)修改這一行數(shù)據(jù)。
事務(wù) A 開啟事務(wù)的時(shí)候會(huì)生成一個(gè) ReadView,所以說這個(gè) ReadView 的創(chuàng)建者就是事務(wù) A,事務(wù) A 的事務(wù) id 是 10,所以 creator_trx_id 就是 10。
此時(shí),總共就只有事務(wù) A、事務(wù) B 這兩個(gè)事務(wù),而且它們都還沒有提交,所以說 m_ids 會(huì)把這兩個(gè)事務(wù) id,10、18 都記錄下來。min_trx_id 是 m_ids 里面的最小值,10、18 中最小的顯然是 10。當(dāng)前最大的事務(wù) id 是 18,那么下一個(gè)事務(wù)的 id 就是 19,max_trx_id 就是 19。
ReadView 生成之后,事務(wù) A 就要去 undo log 版本鏈中讀取值了。
現(xiàn)在只有一條 undo log 日志,但這并不意味著事務(wù) A 就能讀到這條日志的值 X。它要先判斷這行日志的 trx_id 是否小于當(dāng)前事務(wù)的 min_trx_id。看圖我們可以很輕松地發(fā)現(xiàn),日志的 trx_id = 8 小于 ReadView 中 min_trx_id = 10。
這就意味著,這個(gè)事務(wù) A 開始執(zhí)行之前,修改這行數(shù)據(jù)的事務(wù)已經(jīng)提交了,所以事務(wù) A 是可以查到值 X 的。
四、如何基于 ReadView 實(shí)現(xiàn)可重復(fù)讀?
我們繼續(xù)看,事務(wù) A 第一次讀完之后,事務(wù) B 要修改這行數(shù)據(jù)了。undo log 會(huì)為所有寫操作生成日志,所以就會(huì)生成一條 undo log 日志,并且它的 roll_pointer 會(huì)指向上一條 undo log 日志。
緊接著,事務(wù) A 第二次去讀這行數(shù)據(jù)了,情況如下圖所示:
第一次讀的時(shí)候,開啟事務(wù) A 的時(shí)候就生成了一個(gè) ReadView,R
此時(shí)事務(wù) A 第二次去查詢的時(shí)候,先查到的是 trx_id = 18 的那條數(shù)據(jù),它會(huì)發(fā)現(xiàn) 18 比最小的事務(wù)編號(hào) 10 大。那就說明事務(wù)編號(hào)為 18 的事務(wù),有可能它是讀不到的。
?接著就要去 m_ids 里確認(rèn)是否有 18 這條數(shù)據(jù)了。發(fā)現(xiàn)有 18,那就說明在事務(wù) A 開啟事務(wù)的時(shí)候,這個(gè)事務(wù)是沒有提交的,它修改的數(shù)據(jù)就不應(yīng)該被讀到。
事務(wù) A 就會(huì)順著 roll_pointer 指針繼續(xù)往下找,找到了 trx_id = 8 這條日志,發(fā)現(xiàn)這條能讀,讀到的值任然是 x,與第一次讀到的結(jié)果一致。
成功實(shí)現(xiàn)可重復(fù)讀!
?
轉(zhuǎn)載于:https://zhuanlan.zhihu.com/p/166152616
?
(SAW:Game Over!)
總結(jié)
以上是生活随笔為你收集整理的MySQL / 可重复读到底是怎么实现的?图解 ReadView 机制的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: MySQL / schema的概念
- 下一篇: mysql 怎么登陆远程服务器_教你手机