http://blog.sina.com.cn/s/blog_4673e603010111ty.htmlhtml
本文主要介紹mysql中innodb引擎undo-log和事務中MVCC多版本一致性讀的實現。
1. 概述
Innodb引擎的undo日誌是記錄在表空間中單獨的回滾段中。當mysql作update和delete操做的時候,實際的後臺都是先把舊記錄「刪」了,若是是update和insert再把新記錄「插入」進去。
這裏的刪不是真的刪除,而是標識它被刪除了。而插入也不必定是真的插入,不少狀況下是原地覆蓋原來的記錄。
而關於MVCC多版本一致性讀,就是在同一個事務中,用戶只能看到該事務以前已經生效的和該事務自己作的修改。
在innodb中「MVCC多版本一致性讀」功能的實現是基於undo-log的。
mysql
圖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相關函數的使用介紹在該文件的開始部分:
算法
我的總結:sql
即MVCC一致性度是在事務啓動時,獲取當前活躍事務列表。函數
而後對該事務中的select來講,其能夠讀取到在該事務以前已經提交的而且再也不活躍事務列表中的的記錄。指針
而對於在其以後提交的,或者在活躍事務列表中的,則須要從其UNDO log中獲取一個可用的歷史版本。日誌