🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
在上一篇,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 ![](https://box.kancloud.cn/2016-07-22_5791c9c3a3159.gif) 从图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: ![](https://box.kancloud.cn/2016-07-22_5791c9c3bda4a.gif) 接着就带来一个算法,知道一个页号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”