在上一篇,bingxi和alex聊了關于簇描述結構。在本篇,bingxi和alex會討論下簇頁管理。所謂的簇頁,就是用于管理簇結構的頁。
對應的文件為:
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,我們上一篇聊了簇結構,一個簇描述結構大小為40個字節,管理64個頁。這40個字節存儲在什么地方呢?0~63頁是一個簇,那么n*64~(n+1)*64-1也需要一個簇描述結構。嗯,因此可以將第0頁用于存儲這些結構,假設存儲k個。也就是描述了64k個頁,接著第64k這個頁繼續描述接下來的64k個頁,以此類推。如圖1

從圖1中可以看到,首先是0頁管理64k個頁(也就是個k個簇),接著第64k這個頁管理后面的64k個頁,依次類推。
現在轉化為,這個k值是多少?alex,你從代碼里面看看。
”
Alex:“我們看下fsp0fsp.h中宏定義
~~~
/* Number of pages described in a single descriptor page: currently each page
description takes less than 1 byte; a descriptor page is repeated every
this many file pages */
//該值描述一個簇描述頁可以描述的頁數,也就是每XDES_DESCRIBED_PER_PAGE個頁出現一個簇頁,UNIV_PAGE_SIZE的值我們可以知道是16k(也就是16384)。
#define XDES_DESCRIBED_PER_PAGE?????????? UNIV_PAGE_SIZE
~~~
從定義中可以看出,一個簇頁可以描述16384個頁,也就是256個簇描述結構(16384/64=256)。這256個簇占用的大小為10k(256*40=10k)。而一個頁是16k,剩下不到6k的空閑是不使用的。
這里面,我們就可以知道1個簇頁可以描述的頁為16384,對應的大小為256M(16384*16K=256M)。即1個簇頁管理256M。因此,實際對應的簇頁管理見圖2:

接著就帶來一個算法,知道一個頁號n,它對應的簇頁編號是多少。如果n為0~16384-1,則對應的簇頁為0,也就是在0頁中管理編號為n的頁。如果n為16384~2*16384-1,則對應的簇頁為16384,以此類推。我們看下具體的代碼:
~~~
/************************************************************************
Calculates the page where the descriptor of a page resides. */
UNIV_INLINE
ulint
xdes_calc_descriptor_page(
/*======================*/
??????????????????????????? /* out: descriptor page offset */
?????? ulintoffset)??????????? /* in: page offset */
{
?????? ut_ad(UNIV_PAGE_SIZE > XDES_ARR_OFFSET
????????????? + (XDES_DESCRIBED_PER_PAGE / FSP_EXTENT_SIZE) * XDES_SIZE);
//參數offset為我們需要計算的頁號
// XDES_DESCRIBED_PER_PAGE為16384
?????? return(ut_2pow_round(offset, XDES_DESCRIBED_PER_PAGE));
}
/*****************************************************************
Calculates fast a value rounded to a multiple of a power of 2. */
UNIV_INLINE
ulint
ut_2pow_round(
/*==========*/????????? /* out: value of n rounded down to nearest
???????????????????? multiple of m */
?????? ulintn,??? /* in: number to be rounded */
?????? ulintm)?? /* in: divisor; power of 2 */
{
?????? ut_ad(0x80000000UL % m == 0);
//在本例子中,m為16384
// m – 1對應的二進制為00000000000000000111111111111111
//~(m - 1)為11111111111111111000000000000000
// n & ~(m - 1)相當于將n最低的15位置0
//相當于 n-n%16384
?????? return(n & ~(m - 1));
}
~~~
因此,通過函數xdes_calc_descriptor_page就可以知道給定頁所在的簇頁。
接著有帶來一個算法,該給定頁對應的簇描述結構是簇頁的第幾個簇描述結構(從0開始編碼)。見下面的代碼:
~~~
/************************************************************************
Calculates the descriptor index within a descriptor page. */
UNIV_INLINE
ulint
xdes_calc_descriptor_index(
/*=======================*/
??????????????????????????? /* out: descriptor index */
?????? ulintoffset)??????????? /* in: page offset */
{
//步驟1:ut_2pow_remainder的作用是offset % 16384=n
//步驟2:FSP_EXTENT_SIZE的值是64,那么對應的簇描述結構就是n/64
//舉例,假設offset為16386,那么n為2(16386%16384)
//一個簇頁描述16384個頁,第一個簇描述的頁對應的n為0-63,雖然這里n為2,實際上描述的頁號是16384(該簇頁的頁號)+2=16386
//n為2,對應的第0個簇(n/64)
?????? return(ut_2pow_remainder(offset, XDES_DESCRIBED_PER_PAGE) /
???????????????????????????????????????????????? FSP_EXTENT_SIZE);
}
/*****************************************************************
Calculates fast the remainder when divided by a power of two. */
UNIV_INLINE
ulint
ut_2pow_remainder(
/*==============*/? /* out: remainder */
?????? ulintn,??? /* in: number to be divided */
?????? ulintm)?? /* in: divisor; power of 2 */
{
?????? ut_ad(0x80000000UL % m == 0);
?????? return(n & (m - 1));
}
~~~
通過這個代碼,就可以得到給定頁對應簇頁中的第幾個簇描述結構。我們再看下實際的調用,見最后一個函數的調用。
~~~
/************************************************************************
Gets pointer to a the extent descriptor of a page. The page where the extent
descriptor resides is x-locked. If the page offset is equal to the free limit
of the space, adds new extents from above the free limit to the space free
list, if not free limit == space size. This adding is necessary to make the
descriptor defined, as they are uninitialized above the free limit. */
UNIV_INLINE
xdes_t*
xdes_get_descriptor_with_space_hdr(
/*===============================*/
??????????????????????????? /* out: pointer to the extent descriptor,
??????????????????????????? NULL if the page does not exist in the
??????????????????????????? space or if offset > free limit */
?????? fsp_header_t*sp_header,/* in: space header, x-latched */
?????? ulint??????? space,???? /* in: space id */
?????? ulint??????? offset,???? /* in: page offset;
??????????????????????????? if equal to the free limit,
??????????????????????????? we try to add new extents to
??????????????????????????? the space free list */
?????? mtr_t*??????????? mtr)/* in: mtr handle */
{
?????? ulintlimit;
?????? ulintsize;
?????? ulintdescr_page_no;
?????? page_t*?? descr_page;
?????? ut_ad(mtr);
?????? ut_ad(mtr_memo_contains(mtr, fil_space_get_latch(space),
????????????????????????????????????????? MTR_MEMO_X_LOCK));
?????? /* Read free limit and space size */
?????? limit = mtr_read_ulint(sp_header + FSP_FREE_LIMIT, MLOG_4BYTES, mtr);
?????? size? = mtr_read_ulint(sp_header + FSP_SIZE, MLOG_4BYTES, mtr);
?????? /* If offset is >= size or > limit, return NULL */
?????? if ((offset >= size) || (offset > limit)) {
????????????? return(NULL);
?????? }
?????? /* If offset is == limit, fill free list of the space. */
?????? if (offset == limit) {
????????????? fsp_fill_free_list(FALSE, space, sp_header, mtr);
?????? }
?????? descr_page_no = xdes_calc_descriptor_page(offset);
?????? if (descr_page_no == 0) {
????????????? /* It is on the space header page */
????????????? descr_page = buf_frame_align(sp_header);
?????? } else {
????????????? descr_page = buf_page_get(space, descr_page_no, RW_X_LATCH,
mtr);
#ifdef UNIV_SYNC_DEBUG
????????????? buf_page_dbg_add_level(descr_page, SYNC_FSP_PAGE);
#endif /* UNIV_SYNC_DEBUG */
?????? }????
? //看這里
? //第0個簇結構相當于簇頁頭的偏移量為XDES_ARR_OFFSET
? //定義:#define? XDES_ARR_OFFSET????????? (FSP_HEADER_OFFSET + FSP_HEADER_SIZE)
? //FSP_HEADER_OFFSET的值為38,這個是每個頁都有的,在第11篇文章中有描述
? //定義:#define? FSP_HEADER_SIZE??????????? (32 + 5 * FLST_BASE_NODE_SIZE)
? //#define???? FLST_BASE_NODE_SIZE??? (4 + 2 * FIL_ADDR_SIZE)
? //#define???? FIL_ADDR_SIZE?? 6???? /* address size is 6 bytes */
? //因此FSP_HEADER_SIZE為112
? //所以XDES_ARR_OFFSET為38+112=150
?????? return(descr_page + XDES_ARR_OFFSET
?????? ?????? + XDES_SIZE * xdes_calc_descriptor_index(offset));
}
~~~
通過這個函數就可以得到給定頁對應的簇描述結構。這里面需要提示一點的是,FSP_HEADER_SIZE是有意義的,用于描述表空間。
看下該結構的描述:
/*????????????????? SPACE HEADER??????????
???????????????????? ============
?
File space header data structure: this data structure is contained in the
first page of a space. The space for this header is reserved in every extent
descriptor page, but used only in the first. */
?從中可以看出,每個簇頁都有這樣一個結構,但是只有第一個簇頁有效,也就是第0個文件的第0個文件。在如下的配置中,也就是ibdata1文件的第0頁。
[mysqld]
innodb_data_file_path = ibdata1:10M;ibdata2:10M:autoextend
關于描述符,就這么多。具體的細節,建議查看代碼。
”
Bingxi:“ok,今天就這么多吧”
Alex:“ok”