InnoDB记录由三个部分组成,见表1:
表
1
:
InnoDB
的记录组织形式
名称
|
长度
|
Field Start Offsets
|
F*1
或者
(F*2)
个字节
|
Extra Bytes
|
6
个字节
|
Field Contents
|
和记录的实际内容相关
|
备注:
1)
“
F
”是指记录的字段数量。
2)
“
Field Start Offsets
”是一个目录列表,分别指向下一个字段实际存储的偏移值。
3)
“
Extra Bytes
”的长度是不变的,占用
6
个字节。
4)
“
Field Contents
”存放实际的数据。
记录的起点实际上是从“
Field Contents
”的第一个字节开始的,而不是从“
Field Start Offsets
”的第一个字节开始。如果得到一个记录的指针,实际是指向实际记录的起点。因此,如果要得到其它两个部分,只要对指针进行减法操作就行了。比如减去
6
就得到“
Extra Bytes
”的起始地址。只有第三部分,是采用对指针的加法操作来得到相应的字段地址偏移。
1 FIELD START OFFSETS(字段偏移量列表)
这是一个目录列表,每一个目录是一个相对于记录起点的偏移量。这些目录是反向存储的,也就是说第一个字段的偏移量是存储在该列表的最后一个目录。
举个例子:假设现在有三个列。第一个列的长度是
1
,第二个列的长度是
2
,第三个列的长度是
4
。在这种情况下,相应的偏移量分别是:
1
、
3(1+2)
、
7(1+2+4)
。但是因为目录列表是反向存储的,所以存储的内容为:
07
、
03
、
01
。
这里面有两种特殊的情况需要考虑:
1)
每个目录的长度或者为
1
个字节或者为
2
个字节。只有当记录的总长度小于
127
时,才可以设置目录长度为
1
个字节。在“
Extra Bytes
”中会指出目录的长度是一个字节还是两个字节。
2)
偏移值的最高位通常是标志位。
当每个偏移量是一个字节时:
1)
1 bit=0
:字段是非
NULL
,
=1
:字段是
NULL
。
2)
7 bits
:实际的偏移值,范围是
0
到
127
。
当每个偏移量是两个字节时:
1)
1 bit=0
:字段是非
NULL
,
=1
:字段是
NULL
。
2)
1 bit=0
:字段存储在同一页,
=1
:字段存储在不同的页,只有当记录包含大字段对象时才可能出现这一情况。。
3)
14 bits
:实际的偏移值,范围是
0
到
16383
。
2 EXTRA BYTES(额外字节)
额外字节是定长的,占用
6
个字节。见表
2
。
表
2
:
extra bytes
的组织形式
名称
|
长度
|
描述
|
info_bits
|
|
|
()
|
1 bit
|
没有使用
|
()
|
1 bit
|
没有使用
|
delete_flag
|
1 bit
|
当记录已被删除,设置该标志位为
1
。
|
min_rec_flag
|
1 bit
|
如果是最小虚拟记录,设置该标志位为
1
。
|
n_owned
|
4 bits
|
该记录拥有的记录数量
|
heap_no
|
13 bits
|
在索引页的堆中的序号
|
n_fiels
|
10 bits
|
该记录拥有的字段数量,范围为
1
到
1023
。
|
1bytes_offs_flag
|
1 bit
|
如果“
Files Start Offsets
”是
1
字节的,则设为
1
。
|
next 16 bits
|
16 bits
|
指向该页的下一条记录
|
total
|
48 bits
|
|
如果你仅仅打算读取该记录,那么标志位“
1byte_offs_flag
”必须得读的,因为你需要知道目录指针是
1
字节的还是
2
字节的。
当给定记录的起点指针(即指向“
Field Contents
”),得到目录列表头指针(即指向“
Field Start Offsets
”)的步骤如下:
1)
令
X=n_fields
(该数值和目录列表的数量相等)
2) 如果
1byte_offs_flag
等于
0
,就是说每个偏移量需要
2
个字节表示,那么
X=X+2
。
3)
设
X=X+6
,因为“
Extra Bytes
”为
6
个字节。
4)
所以目录列表头指针的地址,等于记录的起始指针减去
X
。
3 FIELD CONTENTS(字段内容)
这个部分用来存放记录的实际数据,按照定义的顺序进行存放。
4 举例
创建一张表:
create table t(field1 varchar(3),field2 varchar(3),field3 varchar(3));
尽管在定义中我们只提供了三个列(字段),但实际记录中包含六个列,因为系统自动增加了三个系统列用于管理。三个系统列分别为:
ROWID
、事务
ID
、会滚段指针。在这个例子中,我们不考虑这三个列。
插入三条数据:
insert into t values('PP','PP',
'PP');
insert into t values('QQ','QQ',
'QQ');
insert into t values('R',NULL,
NULL);
通过
dump
工作可以得到以下的实际存储数据,见表
3
:
表
3
物理存储中记录的组织形式
Address Values in Hexadecimal
|
Values in ASCII
|
0D4280: 00 00 2D 00 84 4F 4F 4F 4F 4F 4F 4F 4F 4F 19 17
|
..-..OOOOOOOOO..
|
0D4290: 15 13 0C 06 00 00 78 0D 02 BF 00 00 00 00 04 21
|
......x........!
|
0D42A0: 00 00 00 00 09 2A 80 00 00 00 2D 00 84 50 50 50
|
.....*....-..PPP
|
0D42B0: 50 50 50 16 15 14 13 0C 06 00 00 80 0D 02 E1 00
|
PPP.............
|
0D42C0: 00 00 00 04 22 00 00 00 00 09 2B 80 00 00 00 2D
|
....".....+....-
|
0D42D0: 00 84 51 51 51 94 94 14 13 0C 06 00 00 88 0D 00
|
..QQQ...........
|
0D42E0: 74 00 00 00 00 04 23 00 00 00 00 09 2C 80 00 00
|
t.....#.....,...
|
0D42F0: 00 2D 00 84 52 00 00 00 00 00 00 00 00 00 00 00
|
.-..R...........
|
对记录进行重新编排,格式如下所示:
19 17 15 13 0C 06 Field Start Offsets /* First Row */
00 00 78 0D 02 BF Extra Bytes
00 00 00 00 04 21 System Column #1
00 00 00 00 09 2A System Column #2
80 00 00 00 2D 00 84 System Column #3
50 50 Field1 'PP'
50 50 Field2 'PP'
50 50 Field3 'PP'
16 15 14 13 0C 06 Field Start Offsets /* Second Row */
00 00 80 0D 02 E1 Extra Bytes
00 00 00 00 04 22 System Column #1
00 00 00 00 09 2B 80 System Column #2
00 00 00 2D 00 84 System Column #3
51 Field1 'Q'
51 Field2 'Q'
51 Field3 'Q'
94 94 14 13 0C 06 Field Start Offsets /* Third Row */
00 00 88 0D 00 74 Extra Bytes
00 00 00 00 04 23 System Column #1
00 00 00 00 09 2C System Column #2
80 00 00 00 2D 00 84 System Column #3
52 Field1 'R'
现在我们对记录进行分析:
1)
对于第一条记录的六个字段,长度分别为:
6
、
6
、
6
、
2
、
2
、
2
。因为每一个偏移量是针对下一个字段的,所以对应的
16
进制偏移值为:
06
、
0c
(6+6)
、
13(6+6+7)
、
15(6+6+7+2)
、
17(6+6+7+2+2)
、
19(6+6+7+2+2)
。因为目录列表是反序的,所以存储的值是:
19
、
17
、
13
、
0c
、
06
。
2
)我们再看第一条记录的“
Extra Bytes
”部分:
00 00 78 0D 02 BF
。第四个字节的
0D
表示二进制是
1101
,
110
是
n_filed
的最后三个
bits
(继续往前面分析,
n_files
对应的二进制为
0000000110
,也就是
6
,说明该记录由
6
个字段组成),
1101
的最后一个
1
指出了
1byte_offs_flag
值为
1
,即每个偏移量占用
1
个字节。第五个字节和第六个字节组成了
02 BF
,这是下一个(第二条)记录起点对应的偏移量(相对于页开始处)。
3
)现在我们来看第三条记录,“
Field Start Offsets
”中的
94
、
94
,对应着字段
field2
以及
field3
。
94
对应的二进制为
1001 0100
,最高位的
1
表示该字段为
NULL
,字节
01 0100
指的是下一个字段的偏移值:
14
。同时,我们可以看到,
NULL
值并没有真正占用“
Field contents
”的空间。