说明:本次测试的版本是mysql 5.7.23 由于本人水平有限,有错误的地方还请大家帮忙指正.
我们知道innodb表的数据是全部存储聚簇索引中的,非主键索引称为二级索引.不管是二级索引还是聚簇索引都是B+树的存储结构.为方便展示,设置innodb_file_per_table=ON
下面介绍聚簇索引和二级索引的存储结构:
1.自定义主键的聚簇索引
叶子节点的索引结构:[主键列][DB_TRX_ID][DB_POLL_PTR][其他创建表的非主键列]
内节点的索引结构:[主键列][PAGE_NO]
其中DB_TRX_ID:表示的事务ID
DB_POLL_PTR:表示的是undo记录的地址,对于怎样解析undo地址,后续介绍.
造测试数据
create table zdr_test_dump(id int(10) unsigned NOT NULL AUTO_INCREMENT,
c1 int(10) unsigned NOT NULL default 0,
c2 char(255) not null,
c3 char(255) not null,
c4 char(255) not null,
PRIMARY KEY(id,c2),
KEY c1(c1,c3));
delimiter ;;
create procedure loop_insert()
BEGIN
set @x =0;
set @uuid1=uuid();
set @uuid2=uuid();
ins: LOOP
set @x = @x + 1;
IF @x = 200 then
leave ins;
END IF;
insert into zdr_test_dump select 0,round(rand()*1024),
@uuid1,concat(@uuid1,@uuid2),concat(@uuid1,@uuid2);
END LOOP ins;
commit;
END;
;;
这里使用自己编译新增的命令,打印该表的聚簇索引.
使用 set global innodb_zdr_dump_indextree=index_id;命令 会将对应的索引结构输出到errlog目录下的一个新.trc 文件中
使用set global innodb_zdr_dump_btreenode='space_id,page_no';命令 会将表空间的space_id的第page_no 个page的内容输出到errlog目录下的.trc文件中
然后使用set global innodb_zdr_dump_indextree=41; 命令 这里的41是zdr_test_dump表的主键索引的INDEX_ID
然后我们看dump_indextree_26_41.trc 文件(26表示的是zdr_test_dump表所属的表空间ID 即space_id 41表示的INDEX_ID)内容
内容解析:
branch:[0x7f43ac514000][3]level:1 表示的是一个B+树的一个节点也就是一个页(page),一个innodb的page 大小有4K 8K 16K 32K 64K 不同的值.每个page都有一个page_no(从0开始)并且该page_no存储在其对应的page页的特定的位置(后续page内容分析时候再做详述).
branch 表示这个节点是内节点,leaf 表示叶子节点
0x7f43ac514000 表示这个节点的内存地址
[3]表示这个节点的page_no,
level 表示这个节点在整个B+树中的层级(level=节点高度-1).
这里我们知道branch:[0x7f43ac514000][3]level:1 是根节点也是B+树的内节点.
我们可以看下内节点的索引结构:
set global innodb_zdr_dump_btreenode='26,3';
查看文件内容
col 0: nm:[id] len: 4 ----col 0 表示记录的字段下标 nm:表示字段名 len:表示字段存储数据的所占的字节数
00 00 00 0a ------字段所存储的数据 十六进制数
我们再使用set global innodb_zdr_dump_btreenode='26,6'; 查看叶子节点的内容
字段解释:
col 2: nm:[DB_TRX_ID] len: 6 ---这条记录最后一次写的事务ID
00 00 00 00 05 07----十六进制
col 3: nm:[DB_ROLL_PTR] len: 7 这条记录对应的undo记录的信息
insert:1 space_id:1 page_no:37 offset:2693 insert表示该条记录是INSERT还是UPDATE offset:表示对应的undo在space_id=1 page_no=37 页中的偏移位置
2.自定义主键的二级非唯一索引
叶子节点KEY列:[非唯一索引列][主键列]
内节点KEY列:[非唯一索引列][主键列][PAGE_NO]
使用set global innodb_zdr_dump_indextree=42;命令 打印二级索引的结构
我们知道page_no=4 是二级索引的内节点同时也是根节点
执行set global innodb_zdr_dump_btreenode='26,4';
我们查看文件内容
内节点KEY列确实为[非唯一索引列][主键列][PAGE_NO]
我们看下非唯一二级索引的叶子节点的记录格式:
执行set global innodb_zdr_dump_btreenode='26,8';
查看文件内容:
叶子节点列确实为[非唯一索引列][主键列]
3.自定义主键的二级唯一索引
叶子节点KEY列:[唯一索引列][主键列]
内节点KEY列:[唯一索引列][主键列][PAGE_NO]
create table zdr_test_dump1(id int(10) unsigned NOT NULL AUTO_INCREMENT,
c1 int(10) unsigned NOT NULL default 0,
c2 char(255) not null,
c3 char(255) not null,
c4 char(255) not null,
PRIMARY KEY(id,c2),
UNIQUE KEY c1(c1,c3));
delimiter ;;
create procedure loop_insert1()
BEGIN
set @x =0;
set @uuid1=uuid();
set @uuid2=uuid();
ins: LOOP
set @x = @x + 1;
IF @x = 200 then
leave ins;
END IF;
insert into zdr_test_dump1 select 0,@x,
@x,concat(@x,@x),concat(@x,@x);
END LOOP ins;
commit;
END;
;;
新增数据之后
执行set global innodb_zdr_dump_indextree=44;命令查询二级唯一索引结构
我们执行set global innodb_zdr_dump_btreenode='27,4';
查看内节点的KEY列
我们执行set global innodb_zdr_dump_btreenode='27,8';
命令查看二级唯一索引的叶子节点内容:
结论:
叶子节点KEY列:[唯一索引列][主键列]
内节点KEY列:[唯一索引列][主键列][PAGE_NO]
4.未定义主键的聚簇索引
叶子节点KEY列:[DB_ROW_ID][DB_TRX_ID][DB_POLL_PTR][表中的其他所有列]
内节点KEY列:[DB_ROW_ID][CHILD_PAGENO]
create table zdr_test_dump_noclust(
c1 int(10) unsigned default 0,
c2 char(255) ,
c3 char(255) ,
c4 char(255) ,
UNIQUE KEY c12(c1,c2));
delimiter ;;
create procedure loop_insert2()
BEGIN
set @x =0;
set @uuid1=uuid();
set @uuid2=uuid();
ins: LOOP
set @x = @x + 1;
IF @x = 200 then
leave ins;
END IF;
insert into zdr_test_dump_noclust select @x,
@x,concat(@x,@x),concat(@x,@x);
END LOOP ins;
commit;
END;
;;
插入数据之后 执行set global innodb_zdr_dump_indextree=45;查看聚簇索引结构
我们知道space_id=28 page_no=3为聚簇索引的内节点
执行set global innodb_zdr_dump_btreenode='28,3';查看内节点内容
其数据为[DB_ROW_ID][CHILD_PAGE_NO]
row#1 del:0 n_owned:0 heap_no:2 current_offs:126 next_record=142
col 0: nm:[DB_ROW_ID] len: 6 ---未定义主键的innodb表,会自动创建一个DB_ROW_ID作为主键,该主键的值是系统内唯一且单调递增,但是对于同一条记录主从库的DB_ROW_ID可能不同,所以建议创建表的过程中都自定义主键
00 00 00 00 02 00
col 1: nm: [child_page_no] len: 4
00 00 00 05
我们执行set global innodb_zdr_dump_btreenode='28,6'; 查看叶子节点的KEY列
结论:
叶子节点KEY列:[DB_ROW_ID][DB_TRX_ID][DB_POLL_PTR][表中的其他所有列]
内节点KEY列:[DB_ROW_ID][CHILD_PAGENO]
5.未定义主键二级唯一索引
叶子节点KEY列:[二级唯一索引列][DB_ROW_ID]
内节点KEY列:[二级唯一索引列][DB_ROW_ID][PAGE_NO]
执行set global innodb_zdr_dump_indextree=46;查看未定义主键的二级唯一索引c12
我们知道space_id=28,page_no=4 是二级唯一索引的内节点
执行set global innodb_zdr_dump_btreenode='28,4';查询二级唯一索引内节点
KEY列是:[二级唯一索引列][DB_ROW_ID][PAGE_NO]
我们知道space_id=28,page_no=9 是二级唯一索引的叶子节点
执行set global innodb_zdr_dump_btreenode='28,9';查看二级唯一索引叶子节点内容
结论:叶子节点KEY列:[二级唯一索引列][DB_ROW_ID]
内节点KEY列:[二级唯一索引列][DB_ROW_ID][PAGE_NO]