MySQL多版本并發(fā)控制機(jī)制(MVCC)-源碼淺析

其運(yùn)行棧為:

handle_one_connection? MySQL的網(wǎng)絡(luò)模型是one request one thread

|-do_handle_one_connection

|-do_command

|-dispatch_command

|-mysql_parse 解析SQL

|-mysql_execute_command

|-execute_sqlcom_select 執(zhí)行select語句

|-handle_select

...一堆parse join 等的操作,當(dāng)前并不關(guān)心

|-*tab->read_record.read_record 讀取記錄

由于mysql默認(rèn)隔離級(jí)別是repeatable_read(RR),所以read_record重載為

rr_sequential(當(dāng)前我們并不關(guān)心select通過index掃描出row之后再通過condition過濾的過程)趟据。繼續(xù)追蹤:

read_record

|-rr_sequential

|-ha_rnd_next

|-ha_innobase::rnd_next 這邊就已經(jīng)到了innodb引擎了

|-general_fetch

|-row_search_for_mysql

|-lock_clust_rec_cons_read_sees 這邊就是判斷并選擇版本的地方

讓我們看下該函數(shù)內(nèi)部:

bool lock_clust_rec_cons_read_sees(const rec_t* rec /*由innodb掃描出來的一行*/,....){

...

// 從當(dāng)前掃描的行中獲取其最后修改的版本trx_id(事務(wù)id)

trx_id = row_get_rec_trx_id(rec, index, offsets);

// 通過參數(shù)(一致性快照視圖和事務(wù)id)決定看到的行快照

return(read_view_sees_trx_id(view, trx_id));

}

read_view的創(chuàng)建過程

我們先關(guān)注一致性視圖的創(chuàng)建過程,我們先看下read_view結(jié)構(gòu):

struct read_view_t{

// 由于是逆序排列券犁,所以low/up有所顛倒

// 能看到當(dāng)前行版本的高水位標(biāo)識(shí),>= low_limit_id皆不能看見

trx_id_t low_limit_id;

// 能看到當(dāng)前行版本的低水位標(biāo)識(shí),< up_limit_id皆能看見

trx_id_t up_limit_id;

// 當(dāng)前活躍事務(wù)(即未提交的事務(wù))的數(shù)量

ulint n_trx_ids;

// 以逆序排列的當(dāng)前獲取活躍事務(wù)id的數(shù)組

// 其up_limit_id<tx_id<low_limit_id

trx_id_t* trx_ids;

// 創(chuàng)建當(dāng)前視圖的事務(wù)id

trx_id_t creator_trx_id;

// 事務(wù)系統(tǒng)中的一致性視圖鏈表

UT_LIST_NODE_T(read_view_t) view_list;

};

然后通過debug,發(fā)現(xiàn)創(chuàng)建read_view結(jié)構(gòu)也是在上述的rr_sequential中操作的汹碱,繼續(xù)跟蹤調(diào)用棧:

rr_sequential

|-ha_rnd_next

|-rnd_next

|-index_first 在start_of_scan為true時(shí)候走當(dāng)前分支index_first

|-index_read

|-row_search_for_mysql

|-trx_assign_read_view

我們看下row_search_for_mysql里的一個(gè)分支:

row_search_for_mysql:

// 這邊只有select不加鎖模式的時(shí)候才會(huì)創(chuàng)建一致性視圖

else if (prebuilt->select_lock_type == LOCK_NONE) { // 創(chuàng)建一致性視圖

trx_assign_read_view(trx);

prebuilt->sql_stat_start = FALSE;

}

上面的注釋就是select for update(in share model)不會(huì)走M(jìn)VCC的原因粘衬。讓我們進(jìn)一步分析trx_assign_read_view函數(shù):

trx_assign_read_view

|-read_view_open_now

|-read_view_open_now_low

好了,終于到了創(chuàng)建read_view的主要階段,主要過程如下圖所示:

代碼過程為:

static read_view_t* read_view_open_now_low(trx_id_t cr_trx_id,mem_heap_t* heap)

{

read_view_t* view;

// 當(dāng)前事務(wù)系統(tǒng)中max_trx_id(即尚未被分配的trx_id)設(shè)置為low_limit_no

view->low_limit_no = trx_sys->max_trx_id;

view->low_limit_id = view->low_limit_no;

// CreateView構(gòu)造函數(shù),會(huì)將非當(dāng)前事務(wù)和已經(jīng)在內(nèi)存中提交的事務(wù)給剔除稚新,即判斷條件為

// trx->id != m_view->creator_trx_id&& !trx_state_eq(trx, TRX_STATE_COMMITTED_IN_MEMORY)的

// 才加入當(dāng)前視圖列表

ut_list_map(trx_sys->rw_trx_list, &trx_t::trx_list, CreateView(view));

if (view->n_trx_ids > 0) {

// 將當(dāng)前事務(wù)系統(tǒng)中的最小id設(shè)置為up_limit_id,因?yàn)槭悄嫘蚺帕?/p>

view->up_limit_id = view->trx_ids[view->n_trx_ids - 1];

} else {

// 如果當(dāng)前沒有非當(dāng)前事務(wù)之外的活躍事務(wù)勘伺,則設(shè)置為low_limit_id

view->up_limit_id = view->low_limit_id;

}

// 忽略purge事務(wù),purge時(shí)褂删,當(dāng)前事務(wù)id是0

if (cr_trx_id > 0) {

read_view_add(view);

}

// 返回一致性視圖

return(view);

}

行版本可見性:

由上面的lock_clust_rec_cons_read_sees可知,行版本可見性由read_view_sees_trx_id函數(shù)判斷:

/*********************************************************************//**

Checks if a read view sees the specified transaction.

@return true if sees */

UNIV_INLINE

bool

read_view_sees_trx_id(

/*==================*/

const read_view_t* view, /*!< in: read view */

trx_id_t trx_id) /*!< in: trx id */

{

if (trx_id < view->up_limit_id) {

return(true);

} else if (trx_id >= view->low_limit_id) {

return(false);

} else {

ulint lower = 0;

ulint upper = view->n_trx_ids - 1;

ut_a(view->n_trx_ids > 0);

do {

ulint mid = (lower + upper) >> 1;

trx_id_t mid_id = view->trx_ids[mid];

if (mid_id == trx_id) {

return(FALSE);

} else if (mid_id < trx_id) {

if (mid > 0) {

upper = mid - 1;

} else {

break;

}

} else {

lower = mid + 1;

}

} while (lower <= upper);

}

return(true);

}

其實(shí)上述函數(shù)就是一個(gè)二分法飞醉,read_view其實(shí)保存的是當(dāng)前活躍事務(wù)的所有事務(wù)id,如果當(dāng)前行版本對(duì)應(yīng)修改的事務(wù)id不在當(dāng)前活躍事務(wù)里面的話,就返回true,表示當(dāng)前版本可見屯阀,否則就是不可見,如下圖所示缅帘。

接上述lock_clust_rec_cons_read_sees的返回:

if (UNIV_LIKELY(srv_force_recovery < 5)

? ? && !lock_clust_rec_cons_read_sees(

? ? rec, index, offsets, trx->read_view)){

// 當(dāng)前處理的是當(dāng)前版本不可見的情況

// 通過undolog來返回到一致的可見版本

err = row_sel_build_prev_vers_for_mysql(

trx->read_view, clust_index,

prebuilt, rec, &offsets, &heap,

&old_vers, &mtr); ? ?

} else{

// 可見,然后返回

}

undolog搜索可見版本的過程

我們現(xiàn)在考察一下row_sel_build_prev_vers_for_mysql函數(shù):

row_sel_build_prev_vers_for_mysql

|-row_vers_build_for_consistent_read

主要是調(diào)用了row_ver_build_for_consistent_read方法返回可見版本:

dberr_t row_vers_build_for_consistent_read(...)

{

......

for(;;){

err = trx_undo_prev_version_build(rec, mtr,version,index,*offsets, heap,&prev_version);

......

trx_id = row_get_rec_trx_id(prev_version, index, *offsets);

// 如果當(dāng)前row版本符合一致性視圖难衰,則返回

if (read_view_sees_trx_id(view, trx_id)) {

......

break;

}

// 如果當(dāng)前row版本不符合钦无,則繼續(xù)回溯上一個(gè)版本(回到for循環(huán)的地方)

version = prev_version;

}

......

}

整個(gè)過程如下圖所示:

至于undolog怎么恢復(fù)出對(duì)應(yīng)版本的row記錄就又是一個(gè)復(fù)雜的過程了,由于篇幅原因召衔,在此略過不表铃诬。

read_view創(chuàng)建時(shí)機(jī)再討論

在創(chuàng)建一致性視圖的row_search_for_mysql的代碼中

// 只有非鎖模式的select才創(chuàng)建一致性視圖

else if (prebuilt->select_lock_type == LOCK_NONE) { // 創(chuàng)建一致性視圖

trx_assign_read_view(trx);

prebuilt->sql_stat_start = FALSE;

}

trx_assign_read_view中由這么一段代碼

// 一致性視圖在一個(gè)事務(wù)只創(chuàng)建一次

if (!trx->read_view) {

trx->read_view = read_view_open_now(

trx->id, trx->global_read_view_heap);

trx->global_read_view = trx->read_view;

}

深圳網(wǎng)站建設(shè)www.sz886.com

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末祭陷,一起剝皮案震驚了整個(gè)濱河市苍凛,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌兵志,老刑警劉巖醇蝴,帶你破解...
    沈念sama閱讀 222,104評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異想罕,居然都是意外死亡悠栓,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,816評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門按价,熙熙樓的掌柜王于貴愁眉苦臉地迎上來惭适,“玉大人,你說我怎么就攤上這事楼镐●荆” “怎么了?”我有些...
    開封第一講書人閱讀 168,697評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵框产,是天一觀的道長(zhǎng)凄杯。 經(jīng)常有香客問我,道長(zhǎng)秉宿,這世上最難降的妖魔是什么戒突? 我笑而不...
    開封第一講書人閱讀 59,836評(píng)論 1 298
  • 正文 為了忘掉前任,我火速辦了婚禮描睦,結(jié)果婚禮上膊存,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好隔崎,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,851評(píng)論 6 397
  • 文/花漫 我一把揭開白布嗡载。 她就那樣靜靜地躺著,像睡著了一般仍稀。 火紅的嫁衣襯著肌膚如雪洼滚。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,441評(píng)論 1 310
  • 那天技潘,我揣著相機(jī)與錄音遥巴,去河邊找鬼。 笑死享幽,一個(gè)胖子當(dāng)著我的面吹牛铲掐,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播值桩,決...
    沈念sama閱讀 40,992評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼摆霉,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了奔坟?” 一聲冷哼從身側(cè)響起携栋,我...
    開封第一講書人閱讀 39,899評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎咳秉,沒想到半個(gè)月后婉支,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,457評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡澜建,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,529評(píng)論 3 341
  • 正文 我和宋清朗相戀三年向挖,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片炕舵。...
    茶點(diǎn)故事閱讀 40,664評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡何之,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出咽筋,到底是詐尸還是另有隱情溶推,我是刑警寧澤,帶...
    沈念sama閱讀 36,346評(píng)論 5 350
  • 正文 年R本政府宣布晤硕,位于F島的核電站悼潭,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏舞箍。R本人自食惡果不足惜舰褪,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,025評(píng)論 3 334
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望疏橄。 院中可真熱鬧占拍,春花似錦略就、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,511評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至贝次,卻和暖如春崔兴,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背蛔翅。 一陣腳步聲響...
    開封第一講書人閱讀 33,611評(píng)論 1 272
  • 我被黑心中介騙來泰國(guó)打工敲茄, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人山析。 一個(gè)月前我還...
    沈念sama閱讀 49,081評(píng)論 3 377
  • 正文 我出身青樓堰燎,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親笋轨。 傳聞我的和親對(duì)象是個(gè)殘疾皇子秆剪,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,675評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容