MySQL系列:innodb源碼分析之page結構解析,mysqlinnodb

來源:互聯網
上載者:User

MySQL系列:innodb源碼分析之page結構解析,mysqlinnodb
 在資料表空間結構分析當中,我們知道innodb的最小實體儲存體分配單位是page頁,在MySQL-3.23版本的源碼中,頁只有兩種頁,一種是index page,一種是undo page。其類型值定義在fil0fil.h當中。                FIL_PAGE_INDEX                         資料索引頁,在資料表空間的inode page和xdes page都是屬於這類。                FIL_PAGE_UNDO_LOG                交易回復日誌頁。在這裡我們主要分析的是 index page,undo log page在事務部分來介紹。不管是index page還是undo log page都是由三部分組成,page_header、page_body、page_trailer三部分組成。針對index page來分析者三部分結構。 1.page headerpage header是page的頭資訊,佔用38個位元組,分別儲存以下資訊:    FIL_PAGE_SPACE            4位元組                        page所屬的資料表空間的space id    FIL_PAGE_OFFSET           4位元組                        page no,一般是在資料表空間的物理位移量    FIL_PAGE_PREV              4 位元組                       前一頁的page no (B+tree的葉子節點是通過鏈表串起來的,有前後關係)    FIL_PAGE_NEXT              4位元組                        後一頁的page no    FIL_PAGE_LSN                 8位元組                        更改記錄時最大的redo log lsn,一般用在redo log恢複時使用    FIL_PAGE_TYPE               2位元組                        page的類型    FIL_PAGE_FILE_FLUSH_LSN 8位元組                    space檔案最後被flush是的redo log lsn,這個值只會在space的第一個頁中被設定    FIL_PAGE_ARCH_LOG_NO 4位元組                      最後被歸檔的archive log file 序號,這個值只會在space的第一個頁中被設定2.page trailer page trailer是在檔案末尾的最後8個位元組, 低位4個位元組是用來表示page頁中資料的checksum,高位4位是用來儲存FIL_PAGE_LSN的部分資訊,關於checksum的計算是通過buf_calc_page_checksum這個函數來結算得到的,基本是通過對page中資料作為參數用ut_fold_binary來快速計算得到。在後續的版本中,page checksum是可以選擇其他演算法來做計算。這兩個字在頁儲存到物理磁碟的時會進行更行,在頁從物理磁碟讀取出來的時候會被校正。宗旨就是保證頁的完整性。3.page body index page body是由5部分組成,分別是body header、recorders、free recorders、free heap和page directory組成。body header的結構定義如下:

#definePAGE_N_DIR_SLOTS     0     /*page directory擁有的slot個數*/#define PAGE_HEAP_TOP         2     /*heap中空閑位置的位移量*/#define PAGE_N_HEAP             4     /*heap中的記錄數,所有分配出去的記錄數,free rec + PAGE_N_RECS + 2*/#define PAGE_FREE                   6     /*指向page中空閑空間的位移量*/#define PAGE_GARBAGE           8     /*已刪除的記錄位元組數,用於重分配*/#define PAGE_LAST_INSERT     10    /*最後插入記錄的位置*/#define PAGE_DIRECTION        12    /*記錄的操作方向,PAGE_LEFT PAGE_RIGHT PAGE_SAME_REC PAGE_SAME_PAGE PAGE_NO_DIRECTION*/#define PAGE_N_DIRECTION    14    /*同一方向連續插入的記錄數*/#define PAGE_N_RECS              16    /*頁中存在的記錄數,不包括infimum和supremum*/#define PAGE_MAX_TRX_ID     18    /*修改當前頁最大的事務ID*/#define PAGE_HEADER_PRIV_END 26#define PAGE_LEVEL                 28     /*當前頁在索引樹的層位置*/#define PAGE_BTR_SEG_LEAF   36     /*B+樹葉子節點所在段的segment header資訊*/define PAGE_BTR_SEG_TOP (36 + FSEG_HEADER_SIZE)     /*B+樹非葉子節點所在段的segment header資訊*/
innodb在把真箇頁可以用的空間當著一個heap,當需要插入記錄的時候,首先會在PAGE FREE中找是否有合適的記錄可以用,如果沒有,就會在PAGE_HEAP_TOP的位移上分配一個指定大小的rec_t的記錄塊,並將記錄案主鍵值插入到recorders當中。那麼recorders是通過什麼樣的方式組織的呢?3.1記錄的組織方式在index page body中,rec(記錄)組織方式採用的是單向鏈表的方式來組織的,最前面一個記錄和最後面一個記錄是innodb定義的虛擬記錄,叫做infimum和supremum。這兩個記錄的物理物質是在body header後面緊接著的連個記錄。其位移如下:
#define PAGE_DATA             (PAGE_HEADER + 36 + 2 * FSEG_HEADER_SIZE)#define PAGE_INFIMUM       (PAGE_DATA + 1 + REC_N_EXTRA_BYTES)           /*本page中索引最小的記錄位置*/#define PAGE_SUPREMUM (PAGE_DATA + 2 + 2 * REC_N_EXTRA_BYTES + 8)     /*本page中索引最大的記錄位置*/
這兩條記錄在index page建立的時候就會被建立,參見page_create函數,其他的記錄是插入在其之間,入下:
3.2body free list除了有效記錄以外,page中還有一類是之間使用過但被刪除的記錄,這類記錄不會直接回收到heap中(因為rec是邏輯循序關聯性進行組織的,無法直接回收到heap中),innodb採用了page free recorders列表來組織和管理,通過body header中的PAGE_FREE來進行定位,PAGE_FREE指向第一個被刪除的rec記錄的頁內位移量。如下:
body header除了用PAGE_FREE來管理釋放的記錄外,還使用了PAGE_GARBAGE來管理其空間大小,這個值表示所有刪除的記錄佔用空間位元組總和,以便刪除的記錄可以重複被使用,提高空間的使用率。除了recorders和free recorders外,還有一個連續的空間,這個空間是用來做記錄分配的,只有當free recorders中沒有合適的記錄空間的時候,才會在這個連續空間上分配記錄。這個空間的地址位移是在PAGE_HEAP_TOP中的。3.3directory slotsinnodb為了快速尋找記錄,在body的後面定義了一個稱之為directory的目錄槽(slots),每個槽位佔用兩個位元組,採用的是逆序儲存,也就是說mifimum的槽位總是在body最後2個位元組上,其他的一次類推。每個槽位可以儲存多個紀錄。以下是各種slot的記錄數描述範圍(n_owned):

Infimum slot owned

只有一條記錄

supremum slot owned

1到8條記錄

普通slot owned

4到8條記錄

如果普通slot在插入新的一條記錄時,普通slot或者supremum管理的記錄數是8,這個時候會對supremum進行split,產生一個slots,所以它的範圍是從4開始。以下是directory的一個關係:
 從上可以看出,slot指向的rec中的owned代表的是向前有多少個rec屬於這個slot管轄,中間被管轄的rec的owned = 0。通過directory的二分尋找只能查到對應記錄所屬的slot,還需要通過owned內部的二分尋找才能精確定位到對應的記錄。這種設計的做法可以減小directory對page空間的佔用,又能有很好尋找的效率。關於slot相關的函數說明:    page_dir_split_slot                        slot分裂函數,當一個slot管轄的範圍插入入新的記錄後超出其最大管理的記                                                           錄數,就會對其進行平均範圍分裂。    page_dir_balance_slot                  slot均衡函數,當一個slot管轄的範圍內有記錄刪除後,其管理的記錄數小於                                                           它最小範圍,就會和鄰近的slot做均衡。  不管是均衡還是分裂,都是最大範圍提高directory儲存空間效率和記錄尋找效率。3.4index page結構關係圖
4頁的操作innodb的index page對記錄的操作主要有3種:尋找記錄、插入記錄、刪除記錄。關於page的操作實現在page0cur.*當中,在這些操作的中,innodb定義了一個page_cur_t,也就是page的遊標,它是個邏輯概念的遊標,只在記憶體中有效。這個page cur是指向當前操作的記錄。定義如下:
typedef struct page_cur_struct    {         byte*rec;/*遊標記錄的指標*/    }page_cur_t;
因為所有的page操作必須將page從物理磁碟讀入到記憶體中進行邏輯頁的構建,再使用page_cur來進行尋找、插入、刪除操作。4.1查詢操作  我們知道在innodb的B+Tree索引搜尋中,只能找到對應記錄所在的index page,那麼找到page後,會在頁中進行記錄尋找,這個頁內尋找過程如下:    1.先通過key在page的directory slots中進行二分尋找,找到key對應的slot    2.因為slot是管理多個記錄(普通的slot owned = [4,8]),所以會再根據KEY在對應的slot管理的記錄中一次二分尋找,直到找到記錄為止。頁內尋找的實現在page0cur.c的page_cur_search_with_match函數當中,這個函數除了返回尋找的記錄以外,還會記錄二分尋找過程中匹配的位元組數和經過的跳數。值得注意的是這個函數支援四種模式的尋找,分別定義如下:
#definePAGE_CUR_G 1        /*大於查詢*/        #definePAGE_CUR_GE 2      /*大於等於查詢*/        #definePAGE_CUR_L 3         /*小於查詢*/        #definePAGE_CUR_LE 4       /*小於等於查詢*/
4.2插入操作在記錄插入之前,會通過要插入記錄KEY找到要插入的位置,尋找的模式是PAGE_CUR_LE,具體步驟如下: 1.通過記錄的key和記錄尋找函數尋找要插入的位置(操作page cur指向插入記錄的前一個記錄)        2.修改前後記錄的關聯關係和插入記錄的關聯關係        3.修改page遊標方向計數器、page last insert        4.修改所在的slot的owned數值,如果超出範圍,進行split slot        5.因為插入記錄是對頁進行修改,所以記錄插入記錄的mtr log。以便異常時對頁的恢複。插入記錄的mtr log構造比較複雜,以下是它的結構:

這裡要解釋的是mismach_index這個變數,innodb為了節省儲存空間,前後兩條記錄會做相同比較,這個變數就是插入的記錄和其前面的記錄從開始位置相同位元組數,這樣rec data是儲存了與之前記錄不同的資料。一條記錄的插入:
整記錄插入過程在page0cur.c中的page_cur_insert_rec_low函數中實現的。
4.3刪除操作  記錄刪除也是首先會通過刪除記錄的key或者記錄地址來確定操作page cur.操作步驟如下:    1.通過記錄資訊確定page cur    2.添加一條刪除記錄的mtr log    3.將記錄前後對應關聯關係進行刪除和更改    4.設定page last insert和其他的頭資訊(n _rec)    5.將記錄插入到body header free列表的起始位置,並修改PAGE_GARBAGE    6.設定所在slot的owned,如果小於管轄範圍的最小值,進行slot的均衡化。刪除的mtr log格式如下:
刪除記錄:

5.小結innodb的index page結構是一個高效利用空間的儲存結構,不僅考慮到查詢的速度,也考慮了合理的利用儲存空間的儲存效率。innodb在這兩者之間找到了比較好的平衡點。頁除了提供基本的插入刪除查詢操作外,還提供批量拷貝記錄、大量刪除記錄等功能。當這些都是基於基本的插入刪除操作之上的。大量操作函數如下:

page_copy_rec_list_end

 將page中的rec之後的記錄全部複製到new page,包括rec

page_copy_rec_list_start

 將page中在rec之前的記錄全部拷貝到new page當中,不包括rec

page_delete_rec_list_end

將page中的rec之後的記錄全部刪除,包括rec

page_delete_rec_list_start 

將page中在rec之前的記錄全部刪除,不包括rec

page_move_rec_list_end

 將page中rec之後的記錄全部move到new page中,包括rec,這些記錄在page是被刪除的

page_move_rec_list_start        

將page中rec之前的記錄全部move到new page中,不包括rec,這些記錄在page是被刪除的

innodb提供這些函數主要是方便上層調用。通過分析page的結構可以很好的理解innodb的記錄組織方式,也有利於去理解B+Tree的索引方式。

相關文章

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.