mvcc的兩種層次的理解

mvcc是什麼

百度百科:Multi-Version Concurrency Control 多版本併發控制,MVCC是一種併發控制的方法,通常在數據庫管理系統中,實現對數據庫的併發訪問。mysql

一種理解

《高性能mysql》Page12
注:這裏說的都是Repeatable Read
能夠認爲mvcc是行級鎖的一個變種,可是他在不少狀況下避免了加鎖操做,所以開銷更低。
mvcc的實現是經過保存數據在某個時間點的快照實現的。也就是說,無論須要執行多長時間,每一個事務看到的數據都是一致的根據事務開始的時間不一樣,每一個事務對同一張表,同一時刻看到的數據多是不同的面試

InnoDb的簡化版行爲:

InnoDb的mvcc,是經過在每行記錄後面保存兩個隱藏的列來實現的。這兩個列,一個保存了行的建立時間,一個保存行的過時時間(或刪除時間)。固然存儲的不是實際的時間值,而是系統版本號。每開始一個新的事務,系統版本號都會自動遞增。事務開始時刻的系統版本號會做爲事務的版本號,用來和查詢到的每行記錄的版本號進行比較。下面看一下,在repeatable read下,mvcc具體是怎麼操做的。spring

SELECT:

根據如下兩個條件檢查每行記錄:
a:只查找版本早於當前事務版本的數據行(也就是,行的系統版本號小於或者等於事務的系統版本號),這樣能夠確保事務讀取的行,要麼是在事務開始以前已經存在的,要麼是事務自身插入或者修改過的。
b:行的刪除版本要麼未定義,要麼大於當前事務版本號。這能夠確保事務讀取到的行,在事務開始以前未被刪除。sql

INSERT:

爲新插入的每一行保存當前系統版本號做爲行版本號。數據庫

DELETE:

爲刪除的每一行保存當前系統版本號做爲行刪除標識。併發

UPDATE:

插入一行新紀錄,保存當前系統版本號做爲行版本號,同時保存當前系統版本號到原來的行做爲行刪除標識。mvc

另外一種理解

掘金小冊《MySQL 是怎樣運行的:從根兒上理解 MySQL》框架

基礎知識:

undo日誌:

每對記錄作改動時,須要把回滾所需的東西記錄下來,好比把記錄的舊值記下來。這些爲了回滾而記錄的東西稱爲undo日誌。性能

trx_id:

行記錄中對這個聚簇索引記錄作改動的語句所在的事務的事務ID。學習

roll_pointer:

本質就是一個指針,指向記錄對應的undo日誌。

undo頁面鏈表:

對每條記錄進行改動前,都須要記錄undo日誌,因此在事務執行過程當中可能產生不少的undo日誌,所以用鏈表來存放。

mvcc版本鏈:

記錄的每次更新,都會將舊值放入一條undo日誌中,隨着更新次數的增多,全部的版本都會被roll_pointer鏈接成一個鏈表,稱之爲版本鏈,版本鏈的頭節點就是當前記錄最新的值。
例子:
咱們如今有這張表,開啓兩個事務,分別作一些操做。

此刻,版本鏈如圖:

因爲須要判斷版本鏈中哪一個版本是當前事務可見的,引出一個概念:

ReadView:

m_ids:生成ReadView時系統中活躍的事務列表。
min_trx_id: m_ids中的最小值。
max_trx_id: 生成ReadView時系統應該分給下一個事務的id值。
creator_trx_id: 生成ReadView的事務的事務id。 (只有增,刪,改時纔會分配事務ID,只讀的化,此值爲0)

版本鏈的每一個節點的可見判斷原則

1:若是被訪問記錄的trx_id與creator_trx_id相同,即當前事務在訪問它本身修改過的記錄,可見。
2:記錄的trx_id小於min_trx_id值,表示生成該版本的事務已經提交,可見。
3:記錄的trx_id大於max_trx_id值,表明生成該版本的事務在當前事務以後纔開啓,不可見。
4:min_trx_id < trx_id < max_trx_id,判斷trx_id是否在m_ids中,若是在,說明建立ReadView時(注意這裏是ReadView,而不是trx_id)該版本的事務仍是活躍的,該版本不能夠被訪問;若是不在,說明建立ReadView時生成該版本的事務已經提交,該版本能夠被訪問。

版本鏈總體的原則

根據版本鏈,依次尋找可見的節點,找到了就返回,若是沒有可見的,說明該條記錄對該事務不可見。
到這裏,是否是感受跟《高性能Mysql》中的mvcc有一點接近了,可是並不同?別急,慢慢來,關鍵就在於ReadView的生成規則。

可重複讀(重點,注意與第一種理解對照)

在第一次讀取時生成ReadView

版本鏈如圖

select分析

第三個事務進行第一個select時,m_ids = [2,3],min_trx_id=2,max_trx_id=4,creator_trx_id=0(由於如今只有讀)。
對於 1 c 2 來講, 2屬於[2,3],不可見。
對於 1 b 2 來講,2屬於[2,3],不可見。
對於1 a 1 來講,1<min_trx_id(即2),可見,所以讀取到的就是a。(這也符合咱們對可重複讀的認知:解決了髒讀。由於事務1沒提交,因此不會讀到其餘事務未提交的數據)

此刻將事務1提交

第二次select分析:

複用以前的ReadView,即m_ids=[2,3],min_trx_id=2,max_trx_id=4,creator_trx_id=0
對於 1 e 3 來講,3屬於[2,3],不可見
對於 1 d 3 來講,3屬於[2,3],不可見
對於 1 c 2 來講,2屬於[2,3],不可見
對於 1 b 3 來講,2屬於[2,3],不可見
對於 1 a 1來講,1<min_trx_id,可見,所以讀到的是a 。(這符合咱們對可重複讀的認知:解決了不可重複讀。)
總結一下,其實很簡單,可重複讀時,第一次select生成ReadView,根據ReadView可見的規則,本事務Begin前的事務都可見,讀取的時刻以後的事務均不可見,之間的事務若是是本身就可見,若是活躍(即select時其餘事務還沒提交)不可見,不活躍(select時其餘事務已提交)可見。
此事務中的非第一次select,其餘事務要麼已經提交(第一次讀時就可見),要麼第一次select以後提交(活躍事務,不可見),這樣就達成了目的:可重複讀。

讀已提交

每次讀取數據前都生成ReadView

第一次select分析

這時的過程跟可重複讀如出一轍。

此刻將事務1提交

第二次select分析:

第二次select,從新生成ReadView,m_ids=[3],min_trx_id=3,max_trx_id=4,creator_trx_id=0;(其實就是min_trx_3由2變成3,m_ids不包括2了)
對於 1 e 3 來講,3屬於[3],不可見
對於 1 d 3 來講,3屬於[3],不可見
對於 1 c 2 來講,2<3,可見
所以讀到的是c,這也符合咱們對讀已提交的理解,第一個事務已經提交了,天然能夠看到他提交的c了。

讀未提交

讀取記錄的最新版本便可

串行化

經過加鎖的方式訪問(之後有空再寫)

總結:面試話術

關於框架,有人跟我說,背背面試題就行,問的就是面試題那些東西,當我研究了一點源碼後,我發現,面試題的總結還真的就是至關精髓,我總結的還不如直接背面試題來的歸納、抽象、精準。並且,本身看+總結,就算當時弄清楚了,面試的時候真的記得住那麼多細節嗎?
就好比隔離級別的幾種表現,ReadView說到底也仍是爲了實現隔離級別,表如今外界看來,那就是隔離級別。我真的能記得住ReadView的細節嗎?我估計是不可能,目測我也就能記住可重複讀只在第一次select時生成ReadView,讀已提交每次select都生成ReadView吧,這是一種快照讀嗎?這是把當時讀取的結果保存下來,下次再讀就直接讀快照嗎?從ReadView的角度來看,固然不是,可是,他表現的不就是快照讀嗎?生不生成那個快照是叫不叫快照讀的關鍵嗎?難道其餘地方所謂的快照讀也不是我想的那麼簡單嗎?我有點迷茫,這些有什麼意義呢?
可是,回憶起我背誦springmvc執行流程的時候,我想我在面試官面前背書的時候,個人眼神必定透露着迷茫,真正看過springmvc執行流程以後,就算我面試用詞不許確,容易遺漏,但我應該是自信的。也許,這就是學習的意義吧。

附註

感受學到了東西的,或者感受我寫的稀巴爛可是想追根朔源想去知識源頭的,強烈建議購買掘金的mysql小冊。 回頭看了下小冊第一章,「還有各位寫博客的同窗,引用的少了叫借鑑,引用的多了就,就有點那個了。但願各位不要大段大段的複製粘貼,用本身的話寫出來的知識才是本身的東西。」,汗,我也不知道我這算不算有點那個了,我儘可能多用用本身的話,多說說本身的理解吧。 sql那段流程跟小冊同樣的,我打算再寫一篇不同流程的具體分析,是一個面試題,儘可能明天或者後天完成。

相關文章
相關標籤/搜索