数据库索引存储结构为什么选择B+树?

306 阅读7分钟

提供给大家一个数据结构学习的网站,全英文的,需要一定的英语基础:

www.cs.usfca.edu/~galles/vis…

目前数据库引擎innodb的数据存储结构为b+树存储结构,那么为什么会采取该存储方式呢?

1.二叉树(Binary Search Trees)

首先看图:

由图看出:

1.第一次插入3,3为根节点

2.再插入4,4变为3的右子节点

3.再插入2,2变为3的左子节点

此前3步为正常左右节点插入

4.再插入5,5变成4的右子节点

5.再插入6,6变成5的右子节点

6.再插入7,7变成6的右子节点

...

如此下去,按照顺序插入,二叉树树高将无限变大,进行数据查询的时候需要从根节点往下依次遍历,所以当插入顺序依次增大时,二叉树的结构就变成了链式结构,链式结构的查询效率想必不用多说。

例:在该情况下依次插入了1,2,3,4,5,6,7,...(递增),100。那么如果我要查询数据100的位置,由于树高为100,那么查找到100需要经过100个节点,进行100次比较。

2.红黑树(Red-Black Trees)

再看图:

由图看出:

1.第一次插入1,1为根节点

2.再插入2,2为1的右子节点

3.再插入5,5为2的右子节点,此时为了保持树的左右节点平衡,树旋转至平衡

4.再插入4,4为5的左子节点

5.再插入3,3为4的左子节点,此时树出现不平衡,树再次旋转至平衡

红黑树也是二叉树的一种,但在插入过程中,树难免出现不平衡的情况,红黑树会自动旋转树至平衡状态,这样就保证“树不会长歪”,这样树高不会变得太长。因此红黑树又称平衡二叉树。

例:在该情况下依次插入了1,2,3,4,5,6,7,...(递增),100。那么如果我要查询数据100的位置,由于树高为7,那么查找到100需要经过7个节点,进行7次比较。与二叉树对比,效率快许多。

3.哈希表(Hash Tables)

这里先说一下哈希(hash)表的定义:哈希表是一种根据关键码去寻找值的数据映射结构,该结构通过把关键码映射的位置去寻找存放值的地方,说起来可能感觉有点复杂,我想我举个例子你就会明白了,最典型的的例子就是字典,大家估计小学的时候也用过不少新华字典吧,如果我想要获取“按”字详细信息,我肯定会去根据拼音an去查找 拼音索引(当然也可以是偏旁索引),我们首先去查an在字典的位置,查了一下得到“安”,结果如下。这过程就是键码映射,在公式里面,就是通过key去查找f(key)。其中,按就是关键字(key),f()就是字典索引,也就是哈希函数,查到的页码4就是哈希值。

但是问题又来了,我们要查的是“按”,而不是“安,但是他们的拼音都是一样的。也就是通过关键字按和关键字安可以映射到一样的字典页码4的位置,这就是哈希冲突(也叫哈希碰撞),在公式上表达就是key1≠key2,但f(key1)=f(key2)。冲突会给查找带来麻烦,你想想,你本来查找的是“按”,但是却找到“安”字,你又得向后翻一两页,在计算机里面也是一样道理的。

  但哈希冲突是无可避免的,为什么这么说呢,因为你如果要完全避开这种情况,你只能每个字典去新开一个页,然后每个字在索引里面都有对应的页码,这就可以避免冲突。但是会导致空间增大(每个字都有一页)。

  既然无法避免,就只能尽量减少冲突带来的损失,而一个好的哈希函数需要有以下特点:

  1.尽量使关键字对应的记录均匀分配在哈希表里面(比如说某厂商卖30栋房子,均匀划分ABC3个区域,如果你划分A区域1个房子,B区域1个房子,C区域28个房子,有人来查找C区域的某个房子最坏的情况就是要找28次)。

  2.关键字极小的变化可以引起哈希值极大的变化。

以上关于哈希表的介绍看不懂没关系,我们只要知道,哈希表能根据哈希函数找到准确的值

所以在我们给表加索引的时候,索引方法可选择HASH,但是现如今哈希表的存储结构依旧不是索引最佳的数据结构选择。为什么呢?选取合适的哈希函数h(key),查找效率的期望是O(1),但是哈希表不适合范围查找、最大最小值查找

4.B树(B Trees

这里有图:

由图可看出,B树的存储方式与红黑树很像,B树其实也是一种二叉树。在红黑树的基础上,怎么把树高变小呢?既然想把纵向变小,那么最直接的办法就是横向变大,每一个节点存储多个值。这些节点的数据都存储在电脑内存空间中,在内存中存储的节点里的数据查找速度相对各个节点遍历的查找方式来说,可以忽略不记。但是因为每个节点都存储在内存中,相对的多花费了内存空间,这就是典型的“空间换时间”。

如果按照B树的数据结构存储索引和数据,数据库的索引和数据存储如下图:

其中数字即为索引,data即为索引的数据(数据库表的一行数据)。

节点中的数据索引从左到右依次递增排列,所以如果要进行索引范围查找,可看下图:

如果需要查找大于2的索引,根据红黑树的特性,找2右边的索引就是查找的范围。但是该范围中既包括了非叶子节点,还有叶子节点的数据,这样的范围查找仍然不是最佳的,这里就引申出了B+树。

5.B+树(B+ Trees

由图可知,B+树的存储方式与B树极为类似,但是所有的索引都在叶子节点里存储了,且每个叶子节点之间都有指针,这样进行范围查询的时候,只要顺序访问指针,提高了区间访问的性能

如果按照B+树的数据结构存储索引和数据,数据库的索引和数据存储如下图:

从这里看出,B+树在B树的存储方式上再次进行了优化。B树在每个非叶子节点上都存储了data,而B+树则只在叶子节点上存储data。Mysql官方默认限制每个节点存储大小为16kb,那么假设索引存储类型为BigInt类型,那么每个索引占8b,索引之间都存储了一个指针(上图数字与数字之间的空格),大小为6b,假设data大小为1kb左右。那么B+树的非叶子节点都能存储约16kb/(8b+6b)≈1170个索引,叶子节点都能存储约16kb/(1kb+8b+6b)≈16个索引,而B树的所有节点只能存储约16kb/(1kb+8b+6b)≈16个索引。那么树高为3的B+树可存储1170×1170×16行数据,同树高的B树只能存储16×16×16行数据。

经过这些优化,B+树比较完美的适应了数据库所需的各种要求。

Mysql官方默认限制每个节点存储大小为16k。sql语句查看:

SHOW GLOBAL STATUS LIKE 'Innodb_page_size';