在上一篇,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 */
ulint offset) /* 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 */
ulint n, /* in: number to be rounded */
ulint m) /* 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 */
ulint offset) /* 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 */
ulint n, /* in: number to be divided */
ulint m) /* 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 */
{
ulint limit;
ulint size;
ulint descr_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”