溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

MVCC詳細(xì)講解

發(fā)布時(shí)間:2020-05-07 16:34:36 來(lái)源:億速云 閱讀:238 作者:Leah 欄目:MySQL數(shù)據(jù)庫(kù)
今天小編給大家分享的是MVCC的詳細(xì)介紹,相信很多人都不太了解,為了讓大家更加了解MVCC,所以給大家總結(jié)了以下內(nèi)容,話不多說(shuō),一起往下看吧。
  1. MVCC的實(shí)現(xiàn),是通過保存數(shù)據(jù)在某個(gè)時(shí)間點(diǎn)的快照來(lái)實(shí)現(xiàn)的,也就是說(shuō),不管需要執(zhí)行多長(zhǎng)時(shí)間,每個(gè)事務(wù)看到的數(shù)據(jù)是一致的,根據(jù)事務(wù)開始的時(shí)間不同,每個(gè)事務(wù)對(duì)同一張表,同一時(shí)刻看到的數(shù)據(jù)可能是不一樣的。
  2. innodb在基于鎖的并發(fā)控制技術(shù)上,實(shí)現(xiàn)了MVCC技術(shù),innodb的MVCC技術(shù)有以下幾個(gè)特點(diǎn):
    • 事務(wù)的標(biāo)識(shí),依靠事務(wù)ID,是一個(gè)全局唯一的64bits數(shù)值
    • 多版本,是元組級(jí)的多版本,而不是oracle實(shí)現(xiàn)的是頁(yè)面級(jí)的多版本
    • 最新的數(shù)據(jù)存儲(chǔ)在數(shù)據(jù)頁(yè)面中,其他數(shù)據(jù)的舊版本存儲(chǔ)在回滾段中
  3. 因?yàn)閕nnodb的多版本是元組級(jí)的版本,所以在每個(gè)記錄上,有一些與并發(fā)和回滾等于事務(wù)相關(guān)的隱含字段
    • DATA_TRX_ID: 6字節(jié)長(zhǎng),表示上一個(gè)執(zhí)行插入或更新操作的事務(wù)
    • DATA_ROLL_PTR: 7字節(jié)長(zhǎng),表示舊版本的數(shù)據(jù)位于回滾段中的位置,指向的是一個(gè)舊版本,只有元組被更新,才有會(huì)新版本產(chǎn)生,舊版本被置于回滾段,因此一致性無(wú)鎖讀操作按照“read view”快照需要讀取舊版本時(shí),只能根據(jù)事務(wù)ID回到回滾段中尋找舊版本
    • DATA_ROW_ID:6字節(jié)長(zhǎng),表示執(zhí)行插入操作后生成的單調(diào)自增長(zhǎng)的行的ID標(biāo)識(shí),如果存在聚集索引,索引項(xiàng)則包括的是這個(gè)DB_ROW_ID值
    • DELETE_BIT: 刪除標(biāo)志位
  4. 位于回滾段中的UNDO日志分為兩種
    • INSERT UNOD LOGS:插入到回滾段的日志,僅用于事務(wù)提交時(shí)使用,當(dāng)事務(wù)提交,則對(duì)應(yīng)的INSETR UNDO LOGS里面的內(nèi)容被清除
    • UPDATE UNDO LOGS:被用于一致性無(wú)鎖讀,為一致性讀提供快照隔離下的可被讀取的老版本數(shù)據(jù),當(dāng)沒有需要滿足一致性讀的快照時(shí),一些老版本數(shù)據(jù)才被清理
  5. MVCC的工作機(jī)制
    • innodb的MVCC,是通過在每行記錄后面保存兩個(gè)隱藏的列來(lái)實(shí)現(xiàn)的,這兩個(gè)列,一個(gè)保存的行的創(chuàng)建時(shí)間,一個(gè)保存行的過期時(shí)間,當(dāng)然存儲(chǔ)的并不是實(shí)際的時(shí)間值,而是系統(tǒng)版本號(hào)(system version number),每開始一個(gè)新的事務(wù),系統(tǒng)版本號(hào)就會(huì)遞增,事務(wù)開始時(shí)刻的系統(tǒng)本號(hào)會(huì)作為事務(wù)的版本號(hào),用來(lái)和查詢到的每行記錄的版本號(hào)進(jìn)行記錄,下面看一個(gè)在REPEATABLE READ隔離級(jí)別下,MVCC是如何操作的
    • SELECT
      • innodb會(huì)根據(jù)以下兩個(gè)條件檢查每行記錄,只有符合下面兩個(gè)條件的記錄,才能返回作為查詢結(jié)果
        1. innodb值只查找系統(tǒng)早已當(dāng)前事務(wù)版本的數(shù)據(jù)行(也就是行的系統(tǒng)版本號(hào)小于或等于事務(wù)的系統(tǒng)版本號(hào)),這樣可以確保事務(wù)讀取的行,要么是在事務(wù)開始前就已經(jīng)存在的,要不是事務(wù)自身插入或者修改過的
        2. 行的刪除版本要不未定義,要不大于當(dāng)前事務(wù)版本號(hào),這可以確保事務(wù)讀取到的行,在事務(wù)開始之前未被刪除
    • INSERT
      • innodb為新插入的每一行保存當(dāng)前系統(tǒng)版本號(hào)作為行版本號(hào)
    • DELETE
      • innodb為刪除的每一行保存當(dāng)前系統(tǒng)版本號(hào)作為行刪除標(biāo)識(shí)
    • UPDATE
      • innodb為插入一條新紀(jì)錄,保存當(dāng)前系統(tǒng)版本號(hào)作為行版本號(hào),同時(shí)保存當(dāng)前系統(tǒng)的版本號(hào)到原先的行作為刪除標(biāo)識(shí)
  6. 保留著兩個(gè)額外系統(tǒng)版本號(hào),使得大多數(shù)讀操作讀不需要加鎖。這樣設(shè)計(jì)使得讀數(shù)據(jù)操作很簡(jiǎn)單,性能很好,并且也能保證只會(huì)讀取到符合標(biāo)準(zhǔn)的行,不足之處是每行記錄都需要額外的存儲(chǔ)空間,需要做更多的行檢查工作,以及一些額外的維護(hù)工作
  7. MVCC只在REPEATABLE READ和READ COMMIT兩個(gè)隔離級(jí)別下工作,其他兩個(gè)隔離級(jí)別都和MVCC不兼容,因?yàn)镽EAD UNCOMMIT總是讀取最新的數(shù)據(jù)行,而不是符合當(dāng)前事務(wù)版本的數(shù)據(jù)行,而SERIALIZEABLE則會(huì)對(duì)所有讀取的行都加鎖
  8. 下面我們就開始案例:
    *MVCC詳細(xì)講解
    • 我們執(zhí)行step7的時(shí)候發(fā)現(xiàn)一個(gè)問題,怎么id=4的查不出來(lái)呢,按照上面的規(guī)則,id=3的事務(wù)ID是2,T3的事務(wù)ID是3,這種情況id=4是可以查出來(lái)的呢,所以說(shuō)上面的規(guī)則肯定還說(shuō)的不是很詳細(xì),有遺落的地方,我們接著往下面看
  9. 我們?cè)谑聞?wù)隔離中可以知道,innodb在執(zhí)行select的時(shí)候會(huì)創(chuàng)建一個(gè)快照
    • 隔離級(jí)別大于等于可重復(fù)讀:事務(wù)塊的所有的SELECT操作都要使用同一個(gè)快照,此快照是在第一個(gè)SELECT操作時(shí)建立的
    • 隔離級(jí)別小于等于已提交讀:事務(wù)塊內(nèi)的所有的SELECT操作分別創(chuàng)建屬于自己的快照,因此每次讀都不同,后面的SELECT操作的讀就可以讀到本次讀之前已經(jīng)提交的數(shù)據(jù)
    • 當(dāng)前數(shù)據(jù)可以看到哪些數(shù)據(jù)就是由這個(gè)快照決定的,快照有下面幾個(gè)屬性,并且可見性也根據(jù)這幾個(gè)屬性來(lái)判斷的
      • m_up_limit_id   一個(gè)快照,有左右邊界,左邊界是最小值,右邊界是最大值,此變量是左邊界
      • m_low_limit_id   右邊界
      • m_createor_trx_id   正在創(chuàng)建事務(wù)的事務(wù)ID
      • trx_ids 快照創(chuàng)建時(shí),處于活動(dòng)即尚未完成的讀寫事務(wù)的集合
    • 舉一個(gè)列子:新建一個(gè)快照,假設(shè)當(dāng)前事務(wù)的事務(wù)ID為6,這時(shí)候讀寫事務(wù)鏈上(這個(gè)是全局的)活動(dòng)的事務(wù)有{3,5,6,10},不包括只讀事務(wù),那么調(diào)用方法創(chuàng)建快照時(shí)就會(huì)把{3,5,10}存儲(chǔ)到當(dāng)前的視圖中的trx_ids(6因?yàn)槭钱?dāng)前事務(wù)的ID,不記錄到視圖中),m_up_limit_id的值是3,m_low_limit__id是10,m_createor_trx_id是6
      • trx_id<m_up_limit_id 可見  意味著是快照之前發(fā)生的事務(wù)
      • trx_id>m_low_limit_id 不可見,意味著是快照之后發(fā)生的事務(wù)
      • trx_ids對(duì)應(yīng)的事務(wù)左右的修改還處于活動(dòng)狀態(tài)即還沒有提交對(duì)當(dāng)前事務(wù)來(lái)說(shuō)不可見
      • trx_id=m_createor_trx_id 可見
      • 公式:通過比較事務(wù)ID的值是否在[m_up_limit_id,m_low_limit_id]區(qū)間的左側(cè),中間,還是右側(cè)判斷是否可見
        • 左側(cè)   可見
        • 中間
          • 在trx_ids中不可見
          • 等于m_createor_trx_id 可見
        • 右側(cè)  不可見
  10. innodb的MVCC技術(shù)中的多版本是根據(jù)UNDO日志來(lái)實(shí)現(xiàn)了,我們?cè)谏厦嬲f(shuō)過,DATA_ROLL_PTR ,表示舊版本的數(shù)據(jù)位于回滾段中的位置。DB_ROLL_PTR的結(jié)構(gòu)如下
    • MVCC詳細(xì)講解
  11. 多版本生成
    • 對(duì)于一個(gè)邏輯上的多版本生成過程,其方式如下
      • 最老的版本,一定是插入操作暫存到UNDO日志的版本(對(duì)于聚集索引,不是記錄的所有字段讀暫存到回滾段,而是主鍵信息被暫存)
      • 更新操作,把舊值存入U(xiǎn)NDO日志。同一個(gè)日志反復(fù)被更新,則每次讀存入一個(gè)舊值(前像)到UNDO日志內(nèi),如此就會(huì)有多個(gè)版本,版本之間,使用DATA_POLL_PTR執(zhí)行根據(jù)的版本,由此所有版本構(gòu)成一個(gè)鏈表,鏈頭是索引上的記錄,鏈尾是首次插入時(shí)生成的UNDO信息。
      • 刪除操作,在UNDO日志中保存刪除標(biāo)志
  12. 多版本查找
    • 對(duì)于聚集索引,可以根據(jù)DATA_ROLL_PTR就可以從回滾段中找出前一個(gè)版本的記錄,并知道此記錄是更新操作還是插入操作生成的,如果是插入操作生成的,則意味著此版本是最原始的版本,即使不可見也沒有必要在繼續(xù)回溯查找舊版本了
    • 但是查找的過程與隔離級(jí)別緊密相關(guān)
      • 如果是未提交讀隔離級(jí)別,根本不去找舊版本,在索引上讀到的記錄就被直接使用
      • 如果不是未提交隔離級(jí)別,則需要進(jìn)入U(xiǎn)NDO回滾段中根據(jù)DATA_POLL_PTR進(jìn)行查找,還要判斷是否可見,如果可見,則返回,如果不可見,則一直根據(jù)DATA_POLL_PTR進(jìn)行查找
    1. 有了前面的補(bǔ)充,我們看下是否能解釋前面step7出現(xiàn)的問題

      • MVCC詳細(xì)講解
      • MVCC詳細(xì)講解
      • 我們?cè)赥2的step8修改了id=1的title,但step 9,step10沒有看到,step9沒看到,大家不會(huì)有什么意外,因?yàn)閟tep9對(duì)應(yīng)的事務(wù)ID小于step8的事務(wù)ID,但step10的事務(wù)ID是大于step8的事務(wù)ID的,那么我們來(lái)分析一下是什么造成的,我們這里不說(shuō)undo log的格式,我們只需要知道根據(jù)undo log我們可以知道記錄的多個(gè)版本
      • 一開始,id=1的記錄是這樣的
      • MVCC詳細(xì)講解
      • T1的step4
        • m_up_limit_id=1,m_low_limit_id=1,m_createor_trx_id=1,trx_ids={},DATA_TRX_ID=0
        • DATA_TRX_ID<m_up_limit_id, 在[m_up_limit_id,m_low_limit_id]的左側(cè),記錄可見
      • T2的step 5
        • m_up_limit_id=1,m_low_limit_id=2,m_createor_trx_id=2,trx_ids={1},DATA_TRX_ID=0
        • DATA_TRX_ID<m_up_limit_id, 在[m_up_limit_id,m_low_limit_id]的左側(cè),記錄可見
      • T3的step 6
        • m_up_limit_id=1,m_low_limit_id=3,m_createor_trx_id=3,trx_ids={1,2},DATA_TRX_ID=0
        • DATA_TRX_ID<m_up_limit_id, 在[m_up_limit_id,m_low_limit_id]的左側(cè),記錄可見
      • step7修改了數(shù)據(jù),這時(shí)候id=1的記錄是這樣的
        • MVCC詳細(xì)講解
      • T2的step8
        *m_up_limit_id=1,m_low_limit_id=2,m_createor_trx_id=2,trx_ids={1},DATA_TRX_ID=2
        • DATA_TRX_ID在[m_up_limit_id,m_low_limit_id]的中間位置,m_createor_trx_id=DATE_TRX_ID,因此可見。所以看到了title=‘商品1 push’的這一行
      • T1的step9
        • m_up_limit_id=1,m_low_limit_id=1,m_createor_trx_id=1,trx_ids={},DATA_TRX_ID=2
        • DATA_TRX_ID在[m_up_limit_id,m_low_limit_id]的右側(cè),記錄不可見
        • 那么接著通過DATE_ROLL_PTR,去undo找到了trx_id=0的這一行
        • DATA_TRX_ID=0,DATA_TRX_ID<m_up_limit_id, 在[m_up_limit_id,m_low_limit_id]的左側(cè),記錄可見
      • step10
        • m_up_limit_id=1,m_low_limit_id=3,m_createor_trx_id=3,trx_ids={1,2},DATA_TRX_ID=2
        • DATA_TRX_ID在[m_up_limit_id,m_low_limit_id]的中間位置,并且在trx_ids中,記錄不可見
        • 接著通過DATE_ROLL_PTR,去undo找到了trx_id=0的這一行
        • DATA_TRX_ID=0, DATA_TRX_ID=0,DATA_TRX_ID<m_up_limit_id, 在[m_up_limit_id,m_low_limit_id]的左側(cè),記錄可見

      關(guān)于MVCC詳細(xì)講解就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的參考價(jià)值,可以學(xué)以致用。如果喜歡本篇文章,不妨把它分享出去讓更多的人看到。


向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI