经典的数据结构B+树

我们在做数据库应用的时候,常常需要对特定的列进行建索引,以加快数据的访问速度,所谓的索引就类似于书的目录,通过书的目录我们能够快速查到特定的章节;同样通过索引,我们可以快速地查询到需要的数据。

优秀的数据结构有很多,对于数据库来说,用什么数据结构来作为索引比较合适那?

哈希表

对于查询来说,第一个想到的是哈希表,hash表,可以在O(1)时间内完成查询性能非常好,哈希表的实现一般是数组+链表组成,数据保存为(key,value)格式,数据来了之后先通过哈希算法计算hash值,然后对整个数组的大小取余获得到槽位,找到对应的槽位之后,如果已经有数据了,就把需要添加的数据通过指针的形式连接到后面,以链表的形式来解决冲突;当然还有些其他解决冲突的办法,比如再次寻址法等。

经典的数据结构B+树_第1张图片
Hash表数据结构图示

注意到hash表一般把hash冲突的数据直接链在槽的后面,如果做范围搜索的话,没办法快速完成,而数据库中我们经常做数据的范围查询,所以不适合。

数组

对于范围查询的情况,其实有个简单的数据结构,那就是数组,比如我们把一个班学生的学号按照顺序保存在数组里面,以学号为下标(假设学号是递增的整数范围不大),那么我们可以根据下标快速取查这个学生,而且给定学号可以通过二分查找,快速找到,还可以做范围查询。数组也有明显的缺点,那就是增加或删除的时候台不方便。

平衡二叉树

对于查询来说,平衡二叉树的性能高,算法复杂读为O(logn),更新的复杂度也是O(logn)。如果按照中序遍历方式的时候,也是有顺序的,但是有个问题就是树的高度太高了,树高了之后指针引用比较多影响性能,更重要的是数据库的数据,完全保存在内存中肯定是不可能的,所以大量的数据需要写在磁盘上,假设有100万个节点的平衡二叉树,树的高度就是log2(100w)得到是20,那么由于一个节点的是保存在一个数据块上的,查找叶子节点的数据,就需要访问20个数据块,就要进行20次左右的磁盘查找操作,而且是随机磁盘访问,一次磁盘寻址就要10ms左右,20次就要200ms,这还仅仅是磁盘寻址时间。寻址多,是因为每个树的节点只能有两个子节点,如果每个树有多个叉那,那就是B+树。

B+树

上面说平衡二叉树这种数据结构作为索引的话两个缺点:
1,不太方便做范围查询。
2,树的高度太高,这样会造成多次磁盘访问,需要降低树的高度。
可以将平衡二叉树改造下:


经典的数据结构B+树_第2张图片
支持范围查询二叉树

这样的二叉树,非叶子节点不保存数据,叶子节点保存数据,并且用链表的把叶子节点串起来,这样在做访问的查询的时候,先搜到到叶子节点,再依次沿着链进行数据比对,直到不满足范围的叶子节点为止。
查找过程如下:


经典的数据结构B+树_第3张图片
支持范围二叉树的查找过程

对于第二个缺点改造如下:
这个改造的方法就是上面说的N叉树,Mysql的InnoDB存储引擎的索引B+树中,这个值是1200左右,树高为4的时候,就可以保存:120012001200个值,考虑到树根的数据块是保存再内存里面的,那么再一个10亿行的表上一个整数索引,最多只需要查询三次磁盘就可以了。
这里面的1200叉树不是随便敲定的,因为一次磁盘读取都至少是一个数据块,一个数据块在linux中默认的为16KB,mysql的bigint为8个字节,指针大小在InnoDB中为6个字节,所以一个数据页最多保存的节点个数为:16*1024/(8+6) = 1200 左右。

顺便说下,Mysql的InnoDB引擎的表,表是根据主键顺序以索引形式存放的,为索引组织表,即索引里面保存着整个记录。

经典的数据结构B+树_第4张图片
索引组织表存储

图中的ID表示建立索引的ID列,R1,R2,R3表示对应的是100这个ID,200这个ID,300这个ID对应的完整记录;叶子节点按照ID的大小顺序通过链表进行链接,这样可以快速完成范围查询;注意叶子节点存放的是最终记录,其他节点存访的是索引字段的ID值。

先聊到这里。

你可能感兴趣的:(经典的数据结构B+树)