12—innodb的簇描述结构

最后更新于:2022-04-01 16:05:02

在上一篇里,bingxi和alex聊了关于innodb的页编号。在本篇,bingxi和alex会讨论下簇描述结构。所谓的簇描述结构,对应的英文描述是extent,表达的意思是一些连续的页。 对应的文件为: 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,在共享存储空间的情况,多个innodb表存储在同一个表空间里面。对单个表而言,存储并不一定是连续的。在上一篇里面提到这样的一个例子: a)创建表1,并插入数据 b)创建表2,并插入数据 c)表1插入数据 d)表2插入数据 如果我们每次分配一个页,就会存储得很凌乱。可能第n页属于t1,n+1页属于t2,n+3页属于t1,n+4页属于t2,…… 这样就会降低io的读写性能,因此我们可以看到在mysql中有簇的概念,这里的簇也就是指extent。簇是连续的页,数量是64页。那么我问下alex,假设T1表新分配了一个簇,某些页用完了,如何标识? ” Alex:“bingxi,我也存在这个疑惑。我们用过代码来看这个问题吧。代码如下: ~~~ /*                  EXTENT DESCRIPTOR                      ================= File extent descriptor data structure: contains bits to tell which pages in the extent are free and which contain old tuple version to clean. */ /*-------------------------------------*/ #define    XDES_ID                     0     /* The identifier of the segment                                    to which this extent belongs */ #define XDES_FLST_NODE        8     /* The list node data structure                                    for the descriptors */ #define    XDES_STATE              (FLST_NODE_SIZE + 8)                                    /* contains state information                                    of the extent */ #define    XDES_BITMAP            (FLST_NODE_SIZE + 12)                                    /* Descriptor bitmap of the pages                                    in the extent */ ~~~ 定义里面的数字是偏移量,我们来画个图看下。 ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-07-22_5791c9c388cf6.gif) 从上面我们可以知道: XDES_ID           //0 XDES_FLST_NODE  //8 XDES_STATE        //20 XDES_BITMAP      //24 这些内容中,我们会产生两个疑问:1)16个字节描述64个页的使用状态,怎么描述?如果只是描述该页是否使用,1个bit位就够了,也就是64个bit位,即8个字节。而实际使用了16个字节,那么是不是可以认为是两个bit位来描述一个页的使用情况。2)每个簇使用40个字节,这些内容存储在什么地方? Bingxi,你来看看。我们在本篇中,先解决第一个问题,第二个问题留到下一篇。 ” Bingxi:“第一个问题,可以理解。 ~~~ //每个页需要两个bit位来描述 #define    XDES_BITS_PER_PAGE      2     /* How many bits are there per page */ //这两个bit位中,第一个bit位标识该页是否在使用 #define    XDES_FREE_BIT         0     /* Index of the bit which tells if                                    the page is free */ //第二个标识位目前没有使用 #define    XDES_CLEAN_BIT             1     /* NOTE: currently not used!                                    Index of the bit which tells if                                    there are old versions of tuples                                    on the page */ ~~~ 每个页使用两个位,那么64个页使用的就是16个字节。我们看一下簇的初始化代码: ~~~ /************************************************************************** Inits an extent descriptor to the free and clean state. */ UNIV_INLINE void xdes_init( /*======*/        xdes_t*   descr,      /* in: descriptor */        mtr_t*     mtr)/* in: mtr */ {        ulinti;        ut_ad(descr && mtr);        ut_ad(mtr_memo_contains(mtr, buf_block_align(descr),                                                  MTR_MEMO_PAGE_X_FIX));        ut_ad((XDES_SIZE - XDES_BITMAP) % 4 == 0); //其中XDES_BITMAP的值为24 // XDES_SIZE的大小为40 //也就是簇描述结构中24字节开始的16个字节全部设置为1        for (i = XDES_BITMAP; i < XDES_SIZE; i += 4) {               mlog_write_ulint(descr + i, 0xFFFFFFFFUL, MLOG_4BYTES, mtr);        }     //设置簇的使用状态为空闲簇        xdes_set_state(descr, XDES_FREE, mtr); }     ~~~ 我们接着看xdes_set_state的实现: ~~~ /************************************************************************** Sets the state of an xdes. */ UNIV_INLINE void xdes_set_state( /*===========*/        xdes_t*   descr,      /* in: descriptor */        ulintstate,       /* in: state to set */        mtr_t*     mtr)/* in: mtr handle */ {        ut_ad(descr && mtr);        ut_ad(state >= XDES_FREE);        ut_ad(state <= XDES_FSEG);        ut_ad(mtr_memo_contains(mtr, buf_block_align(descr),                                                  MTR_MEMO_PAGE_X_FIX));     // descr是该簇的起始指针,相对该指针XDES_STATE的开始4个字节填写status的值        mlog_write_ulint(descr + XDES_STATE, state, MLOG_4BYTES, mtr); } ~~~ 同样的,获取状态也是类似的。我们接着看下xdes_get_n_used函数,该函数表述该簇的页已经使用了多少。 ~~~ /************************************************************************** Returns the number of used pages in a descriptor. */ UNIV_INLINE ulint xdes_get_n_used( /*============*/                      /* out: number of pages used */        xdes_t*   descr,      /* in: descriptor */        mtr_t*     mtr)/* in: mtr */ {        ulinti;        ulintcount      = 0;        ut_ad(descr && mtr);        ut_ad(mtr_memo_contains(mtr, buf_block_align(descr), MTR_MEMO_PAGE_X_FIX)); //对该簇的每一页调用函数xdes_get_bit // xdes_get_bit函数返回对应页的是否使用位 //我们从初始化函数中知道,1表示使用,0表示未使用 //因为如果函数返回的值是false,则表示该页已经使用了,将count加1        for (i = 0; i < FSP_EXTENT_SIZE; i++) {               if (FALSE == xdes_get_bit(descr, XDES_FREE_BIT, i, mtr)) {                      count++;               }        }        return(count);        }     ~~~ 如果所有的页都使用完,那么就表示该页已经使用满。 ~~~ /************************************************************************** Returns true if extent contains no free pages. */ UNIV_INLINE ibool xdes_is_full( /*=========*/                      /* out: TRUE if full */        xdes_t*   descr,      /* in: descriptor */        mtr_t*     mtr)/* in: mtr */ {     //如果该簇使用的页等于64(FSP_EXTENT_SIZE),也就是表示该簇已经满了        if (FSP_EXTENT_SIZE == xdes_get_n_used(descr, mtr)) {               return(TRUE);        }        return(FALSE); } ~~~ 其它的函数类似,这里就不一一列举。作为重点,我们再看一下xdes_set_bit函数。 ~~~ /************************************************************************** Sets a descriptor bit of a page. */ UNIV_INLINE void xdes_set_bit( /*=========*/        xdes_t*   descr,      /* in: descriptor */        ulintbit,   /* in: XDES_FREE_BIT or XDES_CLEAN_BIT */        ulintoffset,     /* in: page offset within extent:                      0 ... FSP_EXTENT_SIZE - 1 */        ibool       val,  /* in: bit value */        mtr_t*     mtr)/* in: mtr */ {        ulintindex;        ulintbyte_index;        ulintbit_index;        ulintdescr_byte;               ut_ad(mtr_memo_contains(mtr, buf_block_align(descr),                                                  MTR_MEMO_PAGE_X_FIX));        ut_ad((bit == XDES_FREE_BIT) || (bit == XDES_CLEAN_BIT));        ut_ad(offset < FSP_EXTENT_SIZE); //假设offset的值为n // XDES_BITS_PER_PAGE为2 //index也就是相对于XDES_BITMAP的偏移bit位        index = bit + XDES_BITS_PER_PAGE * offset; //index/8对应的是相对于XDES_BITMAP的偏移字节        byte_index = index / 8;        //表示所在的位,这里面要重点关注 //字节是从低字节编码的,比如n对应的bit_index是0,实际上表示的是第0位,而不是第7位。即使xxxxxxxy中的y对应的位。 //假设bit_index为6,实际对应的是xyxxxxxx中的y对应的位。 bit_index = index % 8;     //获得对应的字节        descr_byte = mtr_read_ulint(descr + XDES_BITMAP + byte_index,                                                  MLOG_1BYTE, mtr);     //设置对应的bit位 descr_byte = ut_bit_set_nth(descr_byte, bit_index, val);     //重写入        mlog_write_ulint(descr + XDES_BITMAP + byte_index, descr_byte,                                                  MLOG_1BYTE, mtr); }     ~~~ 这样,我们对应簇的bit位进行设置,标识对应的页的使用情况。还有一些其他的函数,建议直接看代码。 ” Alex:“ok,今天就到这里吧。” Bingxi:“ok”
';