Mysql Innodb中undo-log和MVCC多版本一致性讀 的實現

http://hi.baidu.com/gao_dennis/item/1f133311f50a94423a176ef5

http://blog.sina.com.cn/s/blog_4673e603010111ty.html


本文主要介紹mysql中innodb引擎undo-log和事務中MVCC多版本一致性讀的實現。

1. 概述
Innodb引擎的undo日誌是記錄在表空間中單獨的回滾段中。當mysql做update和delete操作的時候,實際的後臺都是先把舊記錄“刪”了,如果是update和insert再把新記錄“插入”進去。

這裏的刪不是真的刪除,而是標識它被刪除了。而插入也不一定是真的插入,很多情況下是原地覆蓋原來的記錄。

而關於MVCC多版本一致性讀,就是在同一個事務中,用戶只能看到該事務之前已經生效的和該事務本身做的修改。
在innodb中“MVCC多版本一致性讀”功能的實現是基於undo-log的。


 

                                                                    圖1
圖1描述了innodb中undo-log和“MVCC多版本一致性讀”功能的基本實現。
Innodb引擎的主鍵索引記錄的頭上包含有6字節的事務ID(DB_TX_ID)與7字節指向回滾段中舊版本的指針(DB_ROLL_PTR)。
當發生修改的之前,innodb會把被修改的字段的原始版本值和它們對應的版本號寫入回滾段中,而指針DB_ROLL_PTR指向的是最新的undo日誌。

當一個事務被創建的時候,innodb會將當前系統中的活躍事務列表(trx_sys->trx_list)創建一個副本(readview),副本中保存的是系統當前不應該被本事務看到的其他事務id列表。當用戶在這個事務中要讀取該行記錄的時候,innodb就會從根據read view,來決定是讀取該行記錄的當前版本,還是需要從undo-log中去尋找更早的版本。

2.undo-log
爲了簡化分析,關於undo-log本文只介紹如下這種情況,也是最普遍的一種情況:
一條update語句,它根據主鍵進行查找,並且不修改主鍵的值。
涉及到的代碼入口在 storage/innobase/btr/btr0cur.c 的btr_cur_update_in_place函數(1821行)

首先記錄undo-log,把本次修改的字段原始值記錄下來:

然後在本條記錄上進行修改:

修改後寫redo-log,redo-log是單獨存放的,存放在名爲ib_logfile的一組文件中:

可以看出以上大體的流程就是先寫undo-log,然後本地修改,最後寫redo-log。

寫undo-log的函數btr_cur_upd_lock_and_undo最終會調用函數trx_undo_page_report_modify(或insert)。(storage/innobase/trx/trx0rec.c 529行)。

把舊版本的事務id寫入undo-log:

把該行的標識字段寫入undo-log:

將舊值保存的undo-log中:


3. MVCC多版本一致性讀
在innodb中,創建一個新事務的時候,innodb會將當前系統中的活躍事務列表(trx_sys->trx_list)創建一個副本(readview),副本中保存的是系統當前不應該被本事務看到的其他事務id列表。當用戶在這個事務中要讀取該行記錄的時候,innodb會將該行當前的版本號與該readview進行比較。
具體的算法如下:
1. 設該行的當前事務id爲trx_id_0,read view中最早的事務id爲trx_id_1,最遲的事務id爲trx_id_2。
2. 如果trx_id_0<trx_id_1的話,那麼表明該行記錄所在的事務已經在本次新事務創建之前就提交了,所以該行記錄的當前值是可見的。跳到步驟6.
3.如果trx_id_0>trx_id_2的話,那麼表明該行記錄所在的事務在本次新事務創建之後纔開啓,所以該行記錄的當前值不可見.跳到步驟5。
4. 如果trx_id_1<=trx_id_0<=trx_id_2,那麼表明該行記錄所在事務在本次新事務創建的時候處於活動狀態,從trx_id_1到trx_id_2進行遍歷,如果trx_id_0等於他們之中的某個事務id的話,那麼不可見。跳到步驟5.
5.從該行記錄的DB_ROLL_PTR指針所指向的回滾段中取出最新的undo-log的版本號,將它賦值該trx_id_0,然後跳到步驟2.
6. 將該可見行的值返回。

關於如何判斷當前系統活躍事務列表中的事務是否應該進入read view(即對當前事務不可見),可以參考read view的創建代碼:storage/innobase/read/read0read.c的函數read_view_open_now。

可以看出主要是排除了當前事務自己,以及目前正在內存中提交的事務。

關於判斷記錄當前值是否可見的代碼位於storage/innobase/include/read0read.ic的函數read_view_sees_trx_id,圖中的view->up_limit_id就是上面的trx_id1,view->low_limit_id就是上面的trx_id2.

下面是關於low_limit_id和up_limit_id的定義(它們的名字看起來容易產生誤解。。。):



4. 後記
關於寫redo-log的代碼入口在:storage/innobase/log/log0log.c的函數log_group_write_buf(1227行)。而關於redo-log相關函數的使用介紹在該文件的開始部分:



個人總結:

即MVCC一致性度是在事務啓動時,獲取當前活躍事務列表。

然後對該事務中的select來說,其可以讀取到在該事務之前已經提交的並且不再活躍事務列表中的的記錄。

而對於在其之後提交的,或者在活躍事務列表中的,則需要從其UNDO log中獲取一個可用的歷史版本。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章