MySQL总结(二)—— Mysql怎么存储的之真正的BTree

本节专注于讨论BTree的结构。 其他如BTree裂变、查找、插入流程、undolog、redolog各种执行顺序等后面讨论。

还是先抛问题。

1.数据结构是什么?代码怎么写的?

2.这个BTree是在内存还是在磁盘里;内存是不是有一部分BTree的结构?

3.联合索引BTree是怎么存储的,多字段查询是怎么检索的?

4.为什么用BTree结构,而不用其他存储结构 比如红黑树?

 

1.简要介绍背景知识

MySQL总结(二)—— Mysql怎么存储的之真正的BTree_第1张图片

借用一副常见的图,mysql一次查询会经过这么多步骤,BTree位于存储引擎层,是Innodb特有的结构。

以此表结构为例。

create table t1 (

`id` int AUTO_INCREMENT;

`a` varchar(255),

`b` varchar(255),

`c` int

primary key (`id`),

key `idx_a_b_c` (`a`,`b`,`c`) 

) ENGINE=InnoDB;

select * from t1 where id = 55;  整个sql语句经过各种流程后,到达存储引擎层 就会被翻译为一个数据结构 [table=t1, id=55]

2.通过ibdata文件分析BTree结构

我们先不抛概念,直接看结构。当前表里有两条记录,利用mysql分析神器(https://github.com/jeremycole/innodb_ruby/wiki 后续文章讲解怎么使用)查看文件内容。

MySQL总结(二)—— Mysql怎么存储的之真正的BTree_第2张图片

MySQL总结(二)—— Mysql怎么存储的之真正的BTree_第3张图片

这个表有两个索引 聚簇索引(Index 489)和普通索引(Index 490)数据。

MySQL总结(二)—— Mysql怎么存储的之真正的BTree_第4张图片

MySQL总结(二)—— Mysql怎么存储的之真正的BTree_第5张图片

我们看聚簇索引存了什么东西:说几个重点结构,1.page header里的level=0 说明是叶子节点 2. records这个结构,几个关键字段 type = clustered 代表是数据行、 offset、next;可以看出是链表方式组织的结构。

我们再插入500条数据。

MySQL总结(二)—— Mysql怎么存储的之真正的BTree_第6张图片

MySQL总结(二)—— Mysql怎么存储的之真正的BTree_第7张图片

可以看出Page增多,BTree不再是一层。对第一个page做分析,可看到records type = node_pointer,说明这个数据行是指针,key字段可以看到是id = 1 child_page_number 说明下一层page号是5。

看到这里 大家结合自身对BTree的了解,是不是已经能体会到真正的结构了 下面开始介绍各种概念。

3.BTree的数据结构 
 

MySQL总结(二)—— Mysql怎么存储的之真正的BTree_第8张图片

如图,BTree核心就是Page和Record,Page里包含一个Records链表,Page通过指针的方式组织成一个BTree。而真正查找要进行一次PageNum查找和一次Record链表查找。

举个例子,比如要查 id = 55;根据结构先找到level =1 (只会有一个page节点)的page,然后遍历record找到第一个小于等于5的record,它的child_page_num指针会告诉下一层page页,找到后再遍历record链表 即可找到记录。

 数据结构

1.Page 

MySQL总结(二)—— Mysql怎么存储的之真正的BTree_第9张图片

就是由一个字节数组组成。

Fil Header

    Page是双向链表,主要包含两个指针 FIL_PAGE_PREV IL_PAGE_NEXT,用来构建这个双向链表。

Page Header

    存由PAGE_LEVEL 树的层级。 0 is leaf node

Infimum + Supremum Records

    一个Page有多个Record,这两个用于保持最小和最大Record(按照index排序) 方便查找

User Records 用户记录

    一个Page最大有65535 16K数据,也就是理论上一个rec就16k(实际远远小于16k),rec也是一个链表,且按照index逻辑有序,page的rec是按照物理顺序存储的。主键作为key,一般order by 就直接按照链表顺序查出来了。

Page Directory

    记录key和record的映射,二分查找。因为BTree是针对Page维度的,最后需要key找到record。

 

4.BTree代码阅读

通篇搜索mysql代码,也没有找到一个struct表述page的数据结构。 这是因为,mysql仅用一个指针来初始化,从文件系统里获取连续一段空间(映射到block_t) 然后用指针初始化这个空间就形成了page结构,然后通过指针来构造树和链表。见上图

涉及数据结构

buf_block_t 一个文件块,取其中的一部分构成page

page_t = typedef byte page_t  本质就是一个字节指针。

涉及代码文件

buf0buf.h

btr0btr.cc

涉及函数

btr_create  //Create the root node for a new index tree. 

    page = page_create(block, mtr, dict_table_is_comp(index->table),page_create_type);

    page_create_low(buf_block_t *block, ulint comp,page_type_t page_type) 初始化一个Page页包括File Header Page Header Infi Supre Records

当第一次插入的时候,会从Page里找,发现没有节点,创建一个record加入到(rec_t)User Records里,你可以认为就是再连续数组后面追加了一些字节。

至此,BTree的数据结构就介绍完了,还有很多其他字段,不展开讲了。 

5.普通索引怎么构建BTree

每个普通索引都有一个BTree,也就是mysql会开辟一块空间存这些Page,leaf page是聚簇索引的key。

select * from t1 where a = "x1" and b = "y1" 究竟会怎么查找呢?

首先,普通索引是key是一个元组(key1的val|key2的val|key3的val),比如记录1 a = "x" b = "y" c = "z"  映射成普通索引record的value 就是[x,y,z]这个内容。顺序是按照a b c顺序存储, 查找的时候一个字段一个字段,根据字符串ASCII码比较。

又插入记录 (x,y2,z) (x1,y,z) (x1,a,z) (x1,y1,z) (x1,y1,a)

最终顺序为: (x,y,z) (x,y2,z) (x1,a,z) (x1,y,z) (x1,y1,a) (x1,y1,z)

MySQL总结(二)—— Mysql怎么存储的之真正的BTree_第10张图片

查找a = "x1" and b = "y1" 流程如下:

pageNumb1  a == x1 and b > y 则选择pageNum3

(x1,y1) > (x1,y) 最后输出 (x1,y1,z) (x1,y1,a)

 

小结:

回答开头三个问题

1.真正的数据结构是什么?也就是代码怎么写的?

Page+Records 树和链表的数据结构

2.这个BTree是在内存还是在磁盘里;内存是不是有一部分BTree的结构

可以看到都存在ibdata文件里;内存里不会有BTree结构 但是会有一些缓存(后面专门讲解)

3.联合索引BTree是怎么存储的,多字段查询是怎么检索的

record的key对应多字段的元组,查找时按照字段比较 找结果。

4.为什么用BTree结构,而不用其他存储结构 比如红黑树? 

磁盘存储,顺序读写,一次IO操作系统一般会预取后几页。B+Tree层级少,同层Page是顺序存储的,查找数据时磁盘寻道时间会少。红黑树相邻节点物理上不相邻,深度很深,每一层都进行一次寻道,IO多。

 

MySQL总结(二)—— Mysql怎么存储的之真正的BTree_第11张图片

磁盘有扇区和磁道(类似于X Y轴),顺序查找不用寻道时间,随机查找需要大量转变扇区和磁道

一个物理地址 = 扇区+磁道

寻道时间: 变动磁头找到磁道

旋转时间:旋转磁盘找到扇区

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

你可能感兴趣的:(数据库,服务器,计算机基础)