1. 变长字段长度列表
记录非NULL变长字段的长度列表; 并且安装列的循序的
逆序进行放置,一列的长度如果小于255,用一字节表示,如果长度长度大于255,则用两字节表示,一字节还是两字节只对一列来讲,如果一行中既有大于255,也有小于255的,则大于255用两字节,小于255则用一字节。从分析文件的十六进制来看,如果大于255,则高字节的最高位是1.
还需要分析代码是否高位都是1,如果是,那变长的最大长度是多少?
【
统计每一个列中数据的长度,在统计这个信息的时候,又有多种情况,主要分定长字段和变长字段,对于定长字段,它的长度直接就是数据类型的长度,比如int类型的那就是4个字节,rowid列就是6个字节等,没有其它附加长度。对于变长字段而言,除了数据内容本身的长度外,还需要计算其数据长度的存储空间,如果字段的字义长度大于255个字节,或者字段的数据类型为BLOB的,那么需要用2个字节来存储这个字段的长度;如果定义长度小于128个字节,或者小于256个字节,但类型不是BLOB类型的,那么这个字段的数据长度用一个字节来存储,除上面2种情况之外,都用2个字节来存储
】
代码中的注释: rem0rec.c rec_get_converted_size_comp_prefix()
If the maximum length of a variable-length field(这个应该是表中定义的长度
)
is up to 255 bytes, the actual length is always stored
in one byte. If the maximum length is more than 255
bytes, the actual length is stored in one byte for
0..127. The length will be encoded in two bytes when
it is 128 or more, or when the field is stored externally.
从这个插入记录(例2)来看, 长度的表达不仅和字段的定义有关, 还和插入的长度有关, 当长度的定义大于255时, 长度大于127时, 就用两个字节来表示(如下面这个表的最后一个字段), 当是127时只用一个字节来表示, 当等于128时就用两个字节来表示(80 80), 第二字节的最高位都是1; 当一个字段的定义小于255时, 它的长度都用一个字节来表示, 不管大于还是小于127.
例1
create table mytest10(t1 VARCHAR(256), t2 VARCHAR(100),
t3 CHAR(50), t4 VARCHAR(300)) ENGINE=INNODB CHARSET=lat
in1 row_format=compact;
INSERT INTO mytest10 SELECT REPEAT('a',256), REPEAT('b', 90),
REPEAT('c', 40), REPEAT('d',300);
INSERT INTO mytest10 SELECT REPEAT('e',20), REPEAT('f', 100),
REPEAT('g', 40), REPEAT('h',300);
INSERT INTO mytest10 SELECT REPEAT('i',20), REPEAT('j', 100),
REPEAT('k', 40), REPEAT('l',100);
INSERT INTO mytest10 SELECT REPEAT('q',200), REPEAT('r', 100),
REPEAT('s', 40), REPEAT('t',150);
INSERT INTO mytest10 SELECT REPEAT('u',128), REPEAT('v', 100), REPEAT('w', 40), REPEAT('x',127);
分析文件的长度列表如下 [上面十进制,下面十六进制]
INSERT INTO mytest10 SELECT REPEAT('a',256), REPEAT('b', 90),
REPEAT('c', 40), REPEAT('d',300);
300
90
256 null标志
2c 81
5a
00 81 00
INSERT INTO mytest10 SELECT REPEAT('e',20), REPEAT('f', 100),
REPEAT('g', 40), REPEAT('h',300);
300
100
20
2c 81
64
14 00
INSERT INTO mytest10 SELECT REPEAT('i',20), REPEAT('j', 100),
REPEAT('k', 40), REPEAT('l',100);
100 100
20
64 64
14 00
INSERT INTO mytest10 SELECT REPEAT('q',200), REPEAT('r', 100),
REPEAT('s', 40), REPEAT('t',150);
150 100
200
96 80 64
c8 80 00
INSERT INTO mytest10 SELECT REPEAT('u',128), REPEAT('v', 100), REPEAT('w', 40), REPEAT('x',127);
127 100
128
7f 64
80 80 00
例2
从这个插入记录来看, 长度的表达不仅和字段的定义有关, 还和插入的长度有关, 当长度的定义大于255时, 长度大于127时, 就用两个字节来表示(如下面这个表的最后一个字段), 当是127时只用一个字节来表示, 当等于128时就用两个字节来表示(80 80); 当一个字段的定义小于255时, 它的长度都用一个字节来表示, 不管大于还是小于127.
create table mytest20(t1 VARCHAR(200), t2 VARCHAR(127), t3 VARCHAR(128), t4 VARCHAR(300)) ENGINE=INNODB CHARSET=lat
in1 row_format=compact;
INSERT INTO mytest20 SELECT REPEAT('e',150), REPEAT('f', 100), REPEAT('g', 128), REPEAT('h',127);
127 128
100
150
7f 80
64
96 00
INSERT INTO mytest20 SELECT REPEAT('i',200), REPEAT('j', 100), REPEAT('k', 128), REPEAT('l',128);
128 128
100
200
80 80 80
64
c8 00
2. NULL标志位
指示了该行数据列中是否有NULL值,这个字段的长度和表的列数有关,每一列对应一个bit位,
低字节对应与高的列,即从右到左,分别对应第一列,第二列. . .(
整个nulls空间中的位图是以从后面向前面的顺序来表示所有nullable列的null信息的。)。
(
A single bit per nullable field to store whether the field is
NULL
, rounded up to a whole number of bytes. If a field is
NULL
its field value will be eliminated from the key or row portion of the record. If no fields are
nullable
, this bitmap is absent.)
create table mytest1(t1 VARCHAR(10), t2 VARCHAR(10), t3 CHAR(10), t4 VARCHAR(10), t5 VARCHAR(10) , t6 VARCHAR(10) , t7 VARCHAR(10), t8 VARCHAR(10), t9 VARCHAR(10), t10 VARCHAR(10), t11 VARCHAR(10), t12 VARCHAR(10), t13 VARCHAR(10), t14 VARCHAR(10), t15 VARCHAR(10), t16 VARCHAR(10), t17 VARCHAR(10), t18 VARCHAR(10)) ENGINE=INNODB CHARSET=latin1 row_format=compact;
INSERT INTO mytest1 VALUES(NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
NULL标志: 03 ff ff
INSERT INTO mytest1 VALUES('a', 'bb', 'cccc', 'ddd', 'eeee', 'ffffff', NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
NULL标志:
03 ff
c0
(000 0011 1111 1111 11
00 0000
)
最后的6位表示 1, 2, 3, 4, 5, 6列不是NULL
INSERT INTO mytest1 VALUES('aaaa', NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
NULL标志:
03 ff f
e
(111
0
)
INSERT INTO mytest1 VALUES(
'a', 'bb',
NULL,
'ddd', 'eeee', 'ffffff',
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
NULL标志:
03 ff c4 (11
00 0
1
00
)
3. 记录头信息
记录信息头的固定部分
Name
|
Size
|
Description
|
info_bits:
|
??
|
??
|
() | 1 bit | unused or unknown |
()
|
1 bit
|
unused or unknown
|
deleted_flag
|
1 bit
|
1 if record is deleted: 该行是否已被删除(
meaning the record is delete-marked (and will be actually deleted by a purge operation in the future).
)
|
min_rec_flag
|
1 bit
|
1 if record is predefined minimum record: 如果为1,如果该记录是预先被定义为最小记录(
meaning this record is the minimum record in a
non-leaf level of the B+Tree
): 对于非叶子页的最左记录,其上有一个标志REC_INFO_MIN_REC_FLAG (0x10UL),而叶子页没有这个标志
|
n_owned
|
4 bits
|
number of records owned by this record:该记录拥有的记录数(
The number of records “owned” by the current record in the page directory.
)
|
heap_no | 13 bits | record's order number in heap of index page 索引堆中该记录的排序记录 |
record_type | 3 bits | 记录类型: 000 普通记录;001 表示B+树节点指针;010 表示Infimum记录;011 表示Supremum记录,1xx 保留 |
next_record | 16 bits | 页中下一条记录的相对位置: 即下一条记录起始地址相对于本记录的偏移。 |
4. 紧接着记录头信息的三个列是系统列:
RowID : 当没有主键时, 这个字段由Innodb自动创建的, 如果有主键时, 就用主键: 6bytes
Transaction ID: 6 bytes
Roll Pointer 7 bytes
Roll Pointer的结构
bits 55: 1 INSERT | 54 ~48 | 47 ~ 16 | 0 ~ 15 |
--------------------------------------------------------------------------------------------------------- ------------------------------------------------
|INSERT or UPDATE | rollback segment ID | undo log segment page number | undo log record offset in undo log segment page |