在上一篇里,bingxi和alex聊了關于innodb的頁編號。在本篇,bingxi和alex會討論下簇描述結構。所謂的簇描述結構,對應的英文描述是extent,表達的意思是一些連續的頁。
對應的文件為:
D:/mysql-5.1.7-beta/storage/innobase/fsp/ fsp0fsp.c
D:/mysql-5.1.7-beta/storage/innobase/include/ fsp0fsp.h
## 1)簇的定義
Bingxi:“alex,在共享存儲空間的情況,多個innodb表存儲在同一個表空間里面。對單個表而言,存儲并不一定是連續的。在上一篇里面提到這樣的一個例子:
a)創建表1,并插入數據
b)創建表2,并插入數據
c)表1插入數據
d)表2插入數據
如果我們每次分配一個頁,就會存儲得很凌亂。可能第n頁屬于t1,n+1頁屬于t2,n+3頁屬于t1,n+4頁屬于t2,……
這樣就會降低io的讀寫性能,因此我們可以看到在mysql中有簇的概念,這里的簇也就是指extent。簇是連續的頁,數量是64頁。那么我問下alex,假設T1表新分配了一個簇,某些頁用完了,如何標識?
”
Alex:“bingxi,我也存在這個疑惑。我們用過代碼來看這個問題吧。代碼如下:
~~~
/*????????????????? EXTENT DESCRIPTOR
???????????????????? =================
File extent descriptor data structure: contains bits to tell which pages in
the extent are free and which contain old tuple version to clean. */
/*-------------------------------------*/
#define??? XDES_ID???????????????????? 0???? /* The identifier of the segment
?????????????????????????????????? to which this extent belongs */
#define XDES_FLST_NODE??????? 8???? /* The list node data structure
?????????????????????????????????? for the descriptors */
#define??? XDES_STATE????????????? (FLST_NODE_SIZE + 8)
?????????????????????????????????? /* contains state information
?????????????????????????????????? of the extent */
#define??? XDES_BITMAP??????????? (FLST_NODE_SIZE + 12)
?????????????????????????????????? /* Descriptor bitmap of the pages
?????????????????????????????????? in the extent */
~~~
定義里面的數字是偏移量,我們來畫個圖看下。

從上面我們可以知道:
XDES_ID? ?????????//0
XDES_FLST_NODE ?//8
XDES_STATE ???????//20
XDES_BITMAP ?????//24
這些內容中,我們會產生兩個疑問:1)16個字節描述64個頁的使用狀態,怎么描述?如果只是描述該頁是否使用,1個bit位就夠了,也就是64個bit位,即8個字節。而實際使用了16個字節,那么是不是可以認為是兩個bit位來描述一個頁的使用情況。2)每個簇使用40個字節,這些內容存儲在什么地方?
Bingxi,你來看看。我們在本篇中,先解決第一個問題,第二個問題留到下一篇。
”
Bingxi:“第一個問題,可以理解。
~~~
//每個頁需要兩個bit位來描述
#define??? XDES_BITS_PER_PAGE????? 2???? /* How many bits are there per page */
//這兩個bit位中,第一個bit位標識該頁是否在使用
#define??? XDES_FREE_BIT???????? 0???? /* Index of the bit which tells if
?????????????????????????????????? the page is free */
//第二個標識位目前沒有使用
#define??? XDES_CLEAN_BIT???????????? 1???? /* NOTE: currently not used!
?????????????????????????????????? Index of the bit which tells if
?????????????????????????????????? there are old versions of tuples
?????????????????????????????????? on the page */
~~~
每個頁使用兩個位,那么64個頁使用的就是16個字節。我們看一下簇的初始化代碼:
~~~
/**************************************************************************
Inits an extent descriptor to the free and clean state. */
UNIV_INLINE
void
xdes_init(
/*======*/
?????? xdes_t*?? descr,????? /* in: descriptor */
?????? mtr_t*???? mtr)/* in: mtr */
{
?????? ulinti;
?????? ut_ad(descr && mtr);
?????? ut_ad(mtr_memo_contains(mtr, buf_block_align(descr),
???????????????????????????????????????????????? MTR_MEMO_PAGE_X_FIX));
?????? ut_ad((XDES_SIZE - XDES_BITMAP) % 4 == 0);
//其中XDES_BITMAP的值為24
// XDES_SIZE的大小為40
//也就是簇描述結構中24字節開始的16個字節全部設置為1
?????? for (i = XDES_BITMAP; i < XDES_SIZE; i += 4) {
????????????? mlog_write_ulint(descr + i, 0xFFFFFFFFUL, MLOG_4BYTES, mtr);
?????? }
??? //設置簇的使用狀態為空閑簇
?????? xdes_set_state(descr, XDES_FREE, mtr);
}????
~~~
我們接著看xdes_set_state的實現:
~~~
/**************************************************************************
Sets the state of an xdes. */
UNIV_INLINE
void
xdes_set_state(
/*===========*/
?????? xdes_t*?? descr,????? /* in: descriptor */
?????? ulintstate,?????? /* in: state to set */
?????? mtr_t*???? mtr)/* in: mtr handle */
{
?????? ut_ad(descr && mtr);
?????? ut_ad(state >= XDES_FREE);
?????? ut_ad(state <= XDES_FSEG);
?????? ut_ad(mtr_memo_contains(mtr, buf_block_align(descr),
???????????????????????????????????????????????? MTR_MEMO_PAGE_X_FIX));
??? // descr是該簇的起始指針,相對該指針XDES_STATE的開始4個字節填寫status的值
?????? mlog_write_ulint(descr + XDES_STATE, state, MLOG_4BYTES, mtr);
}
~~~
同樣的,獲取狀態也是類似的。我們接著看下xdes_get_n_used函數,該函數表述該簇的頁已經使用了多少。
~~~
/**************************************************************************
Returns the number of used pages in a descriptor. */
UNIV_INLINE
ulint
xdes_get_n_used(
/*============*/
???????????????????? /* out: number of pages used */
?????? xdes_t*?? descr,????? /* in: descriptor */
?????? mtr_t*???? mtr)/* in: mtr */
{
?????? ulinti;
?????? ulintcount????? = 0;
?????? ut_ad(descr && mtr);
?????? ut_ad(mtr_memo_contains(mtr, buf_block_align(descr),
MTR_MEMO_PAGE_X_FIX));
//對該簇的每一頁調用函數xdes_get_bit
// xdes_get_bit函數返回對應頁的是否使用位
//我們從初始化函數中知道,1表示使用,0表示未使用
//因為如果函數返回的值是false,則表示該頁已經使用了,將count加1
?????? for (i = 0; i < FSP_EXTENT_SIZE; i++) {
????????????? if (FALSE == xdes_get_bit(descr, XDES_FREE_BIT, i, mtr)) {
???????????????????? count++;
?????? ?????? }
?????? }
?????? return(count);???????
}????
~~~
如果所有的頁都使用完,那么就表示該頁已經使用滿。
~~~
/**************************************************************************
Returns true if extent contains no free pages. */
UNIV_INLINE
ibool
xdes_is_full(
/*=========*/
???????????????????? /* out: TRUE if full */
?????? xdes_t*?? descr,????? /* in: descriptor */
?????? mtr_t*???? mtr)/* in: mtr */
{
??? //如果該簇使用的頁等于64(FSP_EXTENT_SIZE),也就是表示該簇已經滿了
?????? if (FSP_EXTENT_SIZE == xdes_get_n_used(descr, mtr)) {
????????????? return(TRUE);
?????? }
?????? return(FALSE);
}
~~~
其它的函數類似,這里就不一一列舉。作為重點,我們再看一下xdes_set_bit函數。
~~~
/**************************************************************************
Sets a descriptor bit of a page. */
UNIV_INLINE
void
xdes_set_bit(
/*=========*/
?????? xdes_t*?? descr,????? /* in: descriptor */
?????? ulintbit,?? /* in: XDES_FREE_BIT or XDES_CLEAN_BIT */
?????? ulintoffset,???? /* in: page offset within extent:
???????????????????? 0 ... FSP_EXTENT_SIZE - 1 */
?????? ibool?????? val,? /* in: bit value */
?????? mtr_t*???? mtr)/* in: mtr */
{
?????? ulintindex;
?????? ulintbyte_index;
?????? ulintbit_index;
?????? ulintdescr_byte;
??????
?????? ut_ad(mtr_memo_contains(mtr, buf_block_align(descr),
???????????????????????????????????????????????? MTR_MEMO_PAGE_X_FIX));
?????? ut_ad((bit == XDES_FREE_BIT) || (bit == XDES_CLEAN_BIT));
?????? ut_ad(offset < FSP_EXTENT_SIZE);
//假設offset的值為n
// XDES_BITS_PER_PAGE為2
//index也就是相對于XDES_BITMAP的偏移bit位
?????? index = bit + XDES_BITS_PER_PAGE * offset;
//index/8對應的是相對于XDES_BITMAP的偏移字節
?????? byte_index = index / 8;
?????? //表示所在的位,這里面要重點關注
//字節是從低字節編碼的,比如n對應的bit_index是0,實際上表示的是第0位,而不是第7位。即使xxxxxxxy中的y對應的位。
//假設bit_index為6,實際對應的是xyxxxxxx中的y對應的位。
bit_index = index % 8;
??? //獲得對應的字節
?????? descr_byte = mtr_read_ulint(descr + XDES_BITMAP + byte_index,
???????????????????????????????????????????????? MLOG_1BYTE, mtr);
?? ?//設置對應的bit位
descr_byte = ut_bit_set_nth(descr_byte, bit_index, val);
??? //重寫入
?????? mlog_write_ulint(descr + XDES_BITMAP + byte_index, descr_byte,
???????????????????????????????????????????????? MLOG_1BYTE, mtr);
}????
~~~
這樣,我們對應簇的bit位進行設置,標識對應的頁的使用情況。還有一些其他的函數,建議直接看代碼。
”
Alex:“ok,今天就到這里吧。”
Bingxi:“ok”