思考mysql内核之初级系列13---innodb的簇页管理

   在上一篇,bingxialex聊了关于簇描述结构。在本篇,bingxialex会讨论下簇页管理。所谓的簇页,就是用于管理簇结构的页。

对应的文件为:

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,它对应的簇页编号是多少。如果n0~16384-1,则对应的簇页为0,也就是在0页中管理编号为n的页。如果n16384~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_PAGE16384

       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);

 

//在本例子中,m16384

// 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 */

{

//步骤1ut_2pow_remainder的作用是offset % 16384=n

//步骤2FSP_EXTENT_SIZE的值是64,那么对应的簇描述结构就是n/64

//举例,假设offset16386,那么n216386%16384

//一个簇页描述16384个页,第一个簇描述的页对应的n0-63,虽然这里n2,实际上描述的页号是16384(该簇页的页号)+2=16386

//n2,对应的第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_SIZE112

  //所以XDES_ARR_OFFSET38+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

你可能感兴趣的:(思考mysql内核之初级系列)