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

思考mysql内核之初级系列12---innodb的簇描述结构

杨万富的专栏 2010-08-22 10:04:28 浏览 2,321 次

  在上一篇里,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 */

  定义里面的数字是偏移量,我们来画个图看下。

  从上面我们可以知道:

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

{

       ulint i;

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

       ulint state,       /* 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 */

{

       ulint i;

       ulint count      = 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 */

       ulint bit,   /* in: XDES_FREE_BIT or XDES_CLEAN_BIT */

       ulint offset,     /* in: page offset within extent:

                     0 ... FSP_EXTENT_SIZE - 1 */

       ibool       val,  /* in: bit value */

       mtr_t*     mtr) /* in: mtr */

{

       ulint index;

       ulint byte_index;

       ulint bit_index;

       ulint descr_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”

建议继续学习

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