IT技术博客大学习 共学习 共进步

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

杨万富的专栏 2010-09-06 22:20:32 累计浏览 2,883 次

  在上一篇,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”

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/yzyangwanfu/archive/2010/08/27/5844863.aspx

建议继续学习

  1. Innodb IO优化-配置优化 (累计阅读 7,605)
  2. Innodb分表太多或者表分区太多,会导致内存耗尽而宕机 (累计阅读 7,568)
  3. Innodb 表和索引结构 (累计阅读 6,043)
  4. InnoDB线程并发检查机制 (累计阅读 5,605)
  5. Innodb如何使用内存 (累计阅读 5,104)
  6. Innodb文件表空间结构 (累计阅读 5,067)
  7. 快速预热Innodb Buffer Pool的方法 (累计阅读 4,985)
  8. InnoDB的缓存替换策略及其效果 (累计阅读 4,783)
  9. 多版本并发控制:PostgreSQL vs InnoDB (累计阅读 4,564)
  10. InnoDB之Dirty Page、Redo log (累计阅读 4,483)