求教 InnoDB 索引查找方式 , 在线等 .
zkylm 2013-07-07 07:23:22 抱歉 , 这个帖子会比较长 , 务必请高人解答一下小生的困惑 !
这样的 , 小生非常好奇 innodb 中索引的实现 , 所以今天动手实践了一下 INNODB 索引的结构 , 非常困惑其实怎么搜索的 , 小生列出自己的分析过程 , 请高手对对这个过程给我讲解下 , 不甚感激 !!!
MySQL 版本 : 5.5.30
创建表的语句 :
CREATE TABLE `b` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`p` int(11) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=32753 DEFAULT CHARSET=utf8
另外 , 我打开了 innodb_file_per_table ( 方便取 ibd ) :
innodb_file_per_table ON
另外目前内有数据 :
mysql> select count(*) from b;
+----------+
| count(*) |
+----------+
| 16384 |
+----------+
1 row in set (0.00 sec)
-------------------------- 万能的分割线 --------------------------
使用 py_innodb_page_info.py 对其分析 :
page offset 00000000, page type <File Space Header>
page offset 00000001, page type <Insert Buffer Bitmap>
page offset 00000002, page type <File Segment inode>
page offset 00000003, page type <B-tree Node>, page level <0001>
page offset 00000004, page type <B-tree Node>, page level <0000>
page offset 00000005, page type <B-tree Node>, page level <0000>
page offset 00000006, page type <B-tree Node>, page level <0000>
page offset 00000007, page type <B-tree Node>, page level <0000>
page offset 00000008, page type <B-tree Node>, page level <0000>
page offset 00000009, page type <B-tree Node>, page level <0000>
page offset 0000000a, page type <B-tree Node>, page level <0000>
page offset 0000000b, page type <B-tree Node>, page level <0000>
page offset 0000000c, page type <B-tree Node>, page level <0000>
page offset 0000000d, page type <B-tree Node>, page level <0000>
page offset 0000000e, page type <B-tree Node>, page level <0000>
page offset 0000000f, page type <B-tree Node>, page level <0000>
page offset 00000010, page type <B-tree Node>, page level <0000>
page offset 00000011, page type <B-tree Node>, page level <0000>
page offset 00000012, page type <B-tree Node>, page level <0000>
page offset 00000013, page type <B-tree Node>, page level <0000>
page offset 00000014, page type <B-tree Node>, page level <0000>
page offset 00000015, page type <B-tree Node>, page level <0000>
page offset 00000016, page type <B-tree Node>, page level <0000>
page offset 00000017, page type <B-tree Node>, page level <0000>
page offset 00000018, page type <B-tree Node>, page level <0000>
page offset 00000019, page type <B-tree Node>, page level <0000>
page offset 0000001a, page type <B-tree Node>, page level <0000>
page offset 0000001b, page type <B-tree Node>, page level <0000>
page offset 0000001c, page type <B-tree Node>, page level <0000>
page offset 0000001d, page type <B-tree Node>, page level <0000>
page offset 0000001e, page type <B-tree Node>, page level <0000>
page offset 0000001f, page type <B-tree Node>, page level <0000>
page offset 00000020, page type <B-tree Node>, page level <0000>
page offset 00000021, page type <B-tree Node>, page level <0000>
Total number of page: 34:
Insert Buffer Bitmap: 1
File Space Header: 1
B-tree Node: 31
File Segment inode: 1
有 31 个 B-tree Node , 有一个 page level 0001
page offset 00000003, page type <B-tree Node>, page level <0001>
在此小生特意请教第一个问题 : 这个 page level 最高的页是否 B-Tree 的根节点 ? 因为假如只有 10 条数据 , 总大小不超过 16 KB 的话那么它肯定只有一个 B-Tree Node ( 假设 innodb_page_size 为 16KB ) , 这时的 page level 为 0000 . 那么它的索引呢 ? 还是说直接去这个叶子页就可以了?
先放下这个问题 , 将该数据库使用 hexdump 导出为文件具体分析之 :
hexdump -C -v b.ibd>/tmp/b.txt
首先由于好奇 , 我查看了第一个数据页 , 也就是 :
page offset 00000004, page type <B-tree Node>, page level <0000>
根据其 OFFSET 计算其起始位置应为 10000 , 我列出它的 File Header 和 Page Header 部分 , 看一下这个数据页存了多少行记录 :
00010000 35 64 18 57 00 00 00 04 ff ff ff ff 00 00 00 05 |5d.W............|
00010010 00 00 00 00 4d 15 d0 b3 45 bf 00 00 00 00 00 00 |....M...E.......|
00010020 00 00 00 00 00 02 00 49 3a c4 82 40 1d a3 1d 26 |.......I:..@...&|
00010030 00 00 00 05 00 00 01 1f 00 00 00 00 00 00 00 00 |................|
00010040 00 00 00 00 00 00 00 00 00 34 00 00 00 00 00 00 |.........4......|
00010050 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 |................|
最后两位不是 , 最后两位是伪记录 infimum 的 Record Header 的前两位 .
根据 Page Header 中的 PAGE_N_RECS = 011f , 得出 , 该页中共有 287 行记录 .
这里提出第二个疑问 : innodb 的叶子节点的单个节点最多不是可以存放 200 行记录吗 ? 最少 2 行吗 ( 低于两条成单链表了 , 这个我能理解 ) ? 但是最多不是 200 行吗 ? 这里为什么是 287 行 ? 版本 ? 还是我的这个认知错误 , 其实可以容纳更多 ?
OK , 废话不多说 , 转到 page level 为 0001 的数据页 , 也就是索引页 . 我列出其 File Header 和 Page Header 以及其前两条记录 , 想通过前两条记录保存的值来请教大湿它是如何搜索的 . 谢谢 .
0000c000 f7 6d 37 26 00 00 00 03 ff ff ff ff ff ff ff ff |.m7&............|
0000c010 00 00 00 00 4d 22 54 59 45 bf 00 00 00 00 00 00 |....M"TYE.......|
0000c020 00 00 00 00 00 02 00 08 01 fe 80 20 00 00 00 00 |........... ....|
0000c030 01 f6 00 02 00 1d 00 1e 00 00 00 00 00 00 00 00 |................|
0000c040 00 01 00 00 00 00 00 00 00 34 00 00 00 02 00 00 |.........4......|
0000c050 00 02 00 f2 00 00 00 02 00 00 00 02 00 32 01 00 |.............2..|
0000c060 02 00 1a 69 6e 66 69 6d 75 6d 00 07 00 0b 00 00 |...infimum......|
0000c070 73 75 70 72 65 6d 75 6d 10 00 11 00 0d 80 00 00 |supremum........|
0000c080 01 00 00 00 04 00 00 19 00 0d 80 00 02 16 00 00 |................|
0000c090 00 05 00 00 21 00 0d 80 00 05 53 00 00 00 06 04 |....!.....S.....|
当然 , 这里根据 offset 3 得到从 c000 开始 , 所以我截取了该索引页的前两条记录 .
这里提出第三个疑问 : 索引页的 File Header 中的 FIL_PAGE_TYPE , 不应该是 00 03 吗 ? 为什么这里赫然写着 45 bf ( 数据页 ) ?
接下来 , 看到第一条记录 , 根据定义 , File Header + Page Header 共用 38+56=94 字节 , 而其后紧随两条未记录 infimun , 这两条未记录没有 变长列标示也没有 NULL 位标示 , 所以直接为 recoder header + 本生 , 所以 :
01 00 02 00 1a
即为 infimun 的 recoder header , 根据最后两位 , 得到第一条记录为 :
80 00 00 01 00 00 00 04
存储了两个东西 , 主键 , 键值为 1 , 以及所在数据页的 offset 为 00 00 00 04 ( 也就是 10000 ) .
当然根据该条记录的 Recoder Header 的最后两位得出第二条记录为 :
80 00 02 16 00 00 00 05
存储了两个东西主键 , 键值为 : 534 , offset 为 00 00 00 05 .
这里插一个题外话 , 我开始以为我弄错了 , 因为我前面不是得出第一个页面的数据页记录为 287 个嘛 , 因为我的 id 是 auto_increment 的嘛 , 所以我以为这个应该记录的是 288 而不是 534 , 但随后我验证了一下 :
mysql> select count(*) from b where id<534;
+----------+
| count(*) |
+----------+
| 287 |
+----------+
1 row in set (0.00 sec)
验证了得出结论是对的可能因为什么设置导致了 ID 不连续 , 暂且不提 .
那么这里提出最后一个问题 , 也是主题问题 : 索引的结构都看到了 1 , 534 , 如果值位于 1 和 534 之间那么进入 offset 为 00 00 00 04 的页面也就是第一个数据页 , 否则进入第二个 . 首先我这么理解啊对 ?
其次 , 也看到了 , 我 1 万多条记录只有一个索引页 , 这还真的是体现了 B-Tree 在数据库中的高扇出性 . 就我这个例子来说 , 假设索引上有 500 、600 个节点的值 , 那么这个时候搜索某个值是否使用 2 分查找来增加速度 ?
非常感谢各位老湿倾囊相授 !小生在线等 !