思考mysql内核之初级系列14---innodb的旧式记录结构

   在上一篇里,bingxialex聊了关于簇页管理。Innodb的记录分为新旧两种格式,在本篇里,bingxialex会讨论下innodb的旧式记录结构。

对应的文件为:

D:/mysql-5.1.7-beta/storage/innobase/rem/rem0rec.c

D:/mysql-5.1.7-beta/storage/innobase/include/rem0rec.h

D:/mysql-5.1.7-beta/storage/innobase/include/rem0rec.ic

 

1)innodb旧式结构组成

  Bingxi:“alexmysql存储的最基本的结构是记录。B树的内结点和叶结点都是由记录组成。实际存储的内容如下:

内容1:存放字段偏移量,用于指明字段的偏移量。长度为字段数*1或者字段数*2

内容2:长度为6,存放记录的控制信息。

内容3:存放实际的内容(记录指针指向内容3的开始处)

  Alex,你在代码中看下控制信息相关的6个字节的定义。

  Alex:“好的,我们看下rem0rec.ic的中旧式记录的控制结构的定义。

/* Offsets of the bit-fields in an old-style record. NOTE! In the table the

most significant bytes and bits are written below less significant.

 

       (1) byte offset        (2) bit usage within byte

       downward from

       origin ->  1     8 bits pointer to next record

                     2     8 bits pointer to next record

                     3    1 bit short flag

                            7 bits number of fields

                     4     3 bits number of fields

                            5 bits heap number

                     5     8 bits heap number

                     6     4 bits n_owned

                            4 bits info bits

*/

   这个定义是从右往左的,如果转化为从左往右,则如下图所示:

  因此,我们继续看代码,假设我们已经得到一个记录指针p,那么我们如何获得对应的控制信息。

/**********************************************************

The following function is used to get the number of fields

in an old-style record. */

UNIV_INLINE

ulint

rec_get_n_fields_old(

/*=================*/

                     /* out: number of data fields */

       rec_t*     rec) /* in: physical record */

{

       ulint ret;

 

       ut_ad(rec);

   

    //在这里设置断点

       ret = rec_get_bit_field_2(rec, REC_OLD_N_FIELDS,

                            REC_OLD_N_FIELDS_MASK, REC_OLD_N_FIELDS_SHIFT);

       ut_ad(ret <= REC_MAX_N_FIELDS);

       ut_ad(ret > 0);

 

       return(ret);

}    

  设置断点,可以看到rec在此次终端时的值为0x0119808c,打开内存监控输入该地址。

  从指针向前数出6个字节,这六个字节是

00 00 10 13 00 ce

  根据前面的推算,可以得知如下信息:

4bits info:全为0,也就是表该字段有效

4bits n_owned: 值为0

13bits heap_no: 值为2

10bits n_fiels: 值为9

1bit 1bytes_offs_flag: 值为1,因此1个字节可以表示一个偏移

16bits next 16 bits: 值为0xce

  带这这些信息,我们来验证代码,按F11进入rec_get_bit_field_2函数。

/**********************************************************

Gets a bit field from within 2 bytes. */

UNIV_INLINE

ulint

rec_get_bit_field_2(

/*================*/

       rec_t*     rec,  /* in: pointer to record origin */

       ulint offs,       /* in: offset from the origin down */

       ulint mask,      /* in: mask used to filter bits */

       ulint shift)       /* in: shift right applied after masking */

{

       ut_ad(rec);

   

//在本例中

//rec0x0119808c

//offs4

//mask0x000007fe,0000 0111 1111 1110

//shift1

//步骤1:将指针-4 也就是图1中字节3的起始位置,通过与mask的与操作,将与n_fields相关的10个字节“与”出来,将结果右移一位,就得到记录数

       return((mach_read_from_2(rec - offs) & mask) >> shift);

}

  继续往下执行,得到返回值9。获取控制信息其他字节的方法类似。

  我们接着往下看字段偏移量的类型,在这6个控制信息之前存放的是字段偏移量,也就是相对于记录指针的偏移量。

   我们继续进行调试,在rec_1_get_field_start_offs函数设置断点,可以看到rec的值为0x011ac122

  根据控制信息可以知道该记录的字段数为10个。这10个偏移量如下:

34 b0 30 2c 24 20 1c 14 0d 07

  这些偏移量是反向存储的,实际上对应的各字段的长度为:

字段07

字段16 0d-07=6

字段27 14-0d=7

字段38 1c-14=8

字段44 20-1c=4

字段54 24-20=4

字段68 2c-24=8

字段74  (30-2c=4)

字段8:0  (b0的最高位为1,表示该字段为nullb0去掉最高位的0,同样是30)

字段9 4 (34-30=4)

 因此,字段1存储的7是第一个字段的偏移量么?也就是第一个字段开始值是p+7?那么p+0是什么?ok,很明显字段从0开始编码。看下面的代码。

/**********************************************************

Returns the offset of nth field start if the record is stored in the 1-byte

offsets form. */

UNIV_INLINE

ulint

rec_1_get_field_start_offs(

/*=======================*/

                    /* out: offset of the start of the field */

      rec_t*     rec,        /* in: record */

      ulint n)    /* in: field index */

{

       ut_ad(rec_get_1byte_offs_flag(rec));

       ut_ad(n <= rec_get_n_fields_old(rec));

 

    //步骤1:如果是获得第0个字段的起始地址,那么就是0

       if (n == 0) {

 

              return(0);

       }

   

//步骤2:否则调用函数rec_1_get_prev_field_end_info

// rec_1_get_prev_field_end_info的实现为:

// mach_read_from_1(rec - (REC_N_OLD_EXTRA_BYTES + n))

//因此在本例中,假设n1,则返回7

//假设n2,则返回13

       return(rec_1_get_prev_field_end_info(rec, n)

                                          & ~REC_1BYTE_SQL_NULL_MASK);

 }

  这段代码中出现了宏REC_1BYTE_SQL_NULL_MASK,是因为偏移量的最高为表示是否为null

  当偏移量是1字节时,最高位为0,则是非NULL,为1,则该字段是null。其他的7bit用于表示偏移量,因此可以表示的最大偏移量为127

  当偏移量为2字节时,最高位为0,则是非null,为1,则该字段是null,次最高位用于表示是否字段存储在同一页。

  经过重组,本例的记录进行梳理如下:

34 b0 30 2c 24 20 1c 14 0d 07

00 00 20 15 01 bb        //6个字节的控制信息

74 65 73 74 2f 74 31     //test/t1 字段07字节

00 00 00 00 07 04        //字段16字节

00 00 00 00 35 02 50     //字段27字节

00 00 00 00 00 00 00 0e  //字段38字节

80 00 00 02              //字段44字节

00 00 00 01              //字段54字节

00 00 00 00              //字段68字节

00 00 00 00 00 00 00 00  //字段74字节

                         //字段8null

00 00 00 00              //字段94字节

  建议将文件中的旧式记录的函数都阅读下。Bingxi,你知道旧式记录用于什么地方么?而新式的又用在什么地方?

  Bingxi:“默认情况下,5.1.7版本中,数据字典使用还是旧式记录,而用户自己创建的innodb表使用的是新式存储结构。在下一篇里,我们聊下新式记录格式。”

  Alex:“ok

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值