为何 InnoDB 使用 B+ 树

每一种解决方案都是为了解决某一类问题而产生,因此在问为何使用某种方案的时候,其本质就是在探索该方案是用来知足什么样的需求,解决什么样的问题。sql

因此探究 InnoDb 为何使用 B+ 树这个问题,就是要弄清楚 B+ 树是用来知足什么的需求,解决什么样的问题。数据库

要知足什么样的需求

咱们先看一下一些经常使用的 SQL 语句数组

# 根据某个肯定值来查询对应的信息
select id, name, email from user where id = 1;

# 经过区间值查询
select id, name, email from user where id > 12 and id < 20

# 经过范围查询并进行排序
select id, name, email from user where id < 123 order by id desc limit 10;

复制代码

从以上的几个经常使用的 SQL 咱们能够看到在对数据库进行查找数据的过程当中主要有如下三类需求:数据结构

  1. 根据某个值精确快速查找
  2. 根据区间的上下限来快速查找此区间的数据
  3. 查询符合条件的记录并根据某些字段进行排序

因此,须要找到一种符合上面全部需求的方案。目前比较经常使用于查询的数据结构有如下两种:函数

  • 散列表

散列表

散列表(哈希表)是根据是一种根据(key, value)直接进行访问的数据结构,它经过哈希函数将 key 值映射到散列表对应的位置上,查找效率很是高。性能

索引里其中的一种索引类型哈希索引就是基于散列表实现的,假设咱们对名字创建哈希索引,则查找过程以下图所示:spa

对于每一行数据,存储引擎都会对全部的索引列计算一个哈希码(上图散列表的位置),散列表里的每一个元素指向数据行的指针,因为索引自身只存储对应的哈希值,因此索引的结构十分紧凑,而且能够直接根据键值直接找到对应的数据记录,这让哈希索引查找速度很是快!可是哈希索引也有它的劣势,具体以下:指针

  1. 只有精确匹配索引全部列的查询才有效,好比我在列(name, address)创建哈希索引,若是只查询数据列 name, 则没法使用该索引。
  2. 哈希索引不是按照索引值顺序存储的,即 key 通过哈希函数计算后的哈希值不是按顺序的,因此也就没法用于排序,就不能根据区间进行查找。
  3. 哈希索引只支持等值比较查询,如 = 和 in(),不支持范围的查找,如 id > 17。

因此,哈希索引只适用于特定场合,在适当的场景使用,的确能带来很大的性能提高。好比在 InnoDB 里,就有一种特殊的功能叫 “自适应哈希索引”,若是 InnoDB 注意到某些索引列值被频繁使用时,它会在内存基于 B+ 树索引之上再建立一个哈希索引,这样就能让 B+ 树也具备哈希索引的优势。code

因此散列表结构没法知足上文提到的需求。cdn

​接着咱们来看看树。

平衡二叉树

平衡二叉树可用于查找,且其查找的时间复杂度近似 O(log2n),可是能够用平衡二叉树做为索引的结构吗?

答案是不能。

由于数据库表的数据一般是不少的,正常都是存放在磁盘上的。而磁盘的速度相比内存的速度是慢不少倍的,因此要尽可能减小读取磁盘的次数,经过从内存读取数据来提升速度。

那么,如何将尽可能多且有效的索引数据放到内存中呢?

这里有两个问题要解决:

一、尽可能多

读取磁盘数据的时候,都是按磁盘块来读取的(局部性原理与磁盘预读),并非一条一条的读。在使用树这种结构做为索引的数据结构时,咱们每查找一次数据就须要从磁盘中读取一个树节点,也就是对应的一个磁盘块,因此若是咱们能把尽可能多的数据放到磁盘块中,那么每次读取的数据就会较多。

而平衡二叉树是每一个节点只存储一个键值和数据,也就是说,存储的时候,每一个磁盘块只存储一个键值和数据。

那若是存储了海量的数据,能够想象平衡二叉树的节点将会很是多,树高也会极其高,在查找数据的时候就会进行不少次磁盘 IO,效率将会极低。

因此平衡二叉树没法解决存储尽可能多的索引到内存中这个问题。

二、有效的索引数据

咱们所说的平衡二叉树,指的是逻辑结构上的平衡二叉树,其物理实现是数组。因此在逻辑相近的节点上,其物理位置可能相差会很远。所以,每次读取的磁盘页数据,不少多是用不上的,即有效的索引数据并很少,因此在查找过程当中仍是要进行许屡次的磁盘读取操做。

因此平衡二叉树也没法解决这个问题。

因此,能解决这两个问题的数据结构 —— B 树就被发明出来了。

B 树

B 树(Balance Tree),即平衡树的意思。B 树是从平衡二叉树演化而来,B树的每一个节点能够存储多个关键字,它将节点大小设置为磁盘页的大小,充分利用了磁盘预读的功能。每次读取磁盘页时就会读取一整个节点。也正因每一个节点存储着很是多个关键字,树的深度就会很是的小。进而要执行的磁盘读取操做次数就会很是少,更多的是在内存中对读取进来的数据进行查找。B 树的结构示例以下图所示:

因为 B 树的每个节点,即每个磁盘块存储的数据较多,因此必定程度上解决了上文提到的存储尽可能多的索引的问题。也必定程度上的解决了存储尽可能多的有效索引的问题。

可是,B 树只是必定程度上的解决了问题,咱们须要更好的解决问题。即能不能的作到存储更多的有效的索引呢?

答案是能够。这时候就就须要 B+ 树闪亮登场了。

更好的解决了问题的 B+ 树

B 树必定程度上的解决了问题,而从 B 树演化而来的 B+ 树能更好的解决问题,因此现实使用中几乎已经没有使用 B 树的状况了。

B + 树的结构示意图以下:

那么 B+ 树和 B 树有哪些不一样?

  • 在 B+ 树中,非叶子节点上是不存储数据的,仅存储键值。

由于在数据库中页的大小是固定的,InnoDB 中页的默认大小是 16 KB,若是不存储数据,那么节点就能够存储更多的键值,相应的树的阶树就会更大,对于一样的数据量来讲,须要的树高就会变低,树会更矮胖,如此一来查找数据的时候进行磁盘的 IO 次数就会减小,提高查询效率。

因为 B+ 树的阶数等于键值数量,假设 B+ 树的一个节点能够存储 1000 个键值,那么 3 层的 B+ 树 能够存储 1000 x 1000 x 1000 = 10亿个数据。而且通常根节点是常驻内存的,因此查找 10 亿个数据,只须要 2 次磁盘 IO。

B+ 这个特色很好的解决了上文提到的存储尽可能多的索引数据的问题,而且查询效率也高。

  • B+ 树的叶子节点中的索引数据是按顺序排列的,而且叶子节点间是经过双向链表进行链接的。

这个特色使 B+ 树在实现范围查找,排序查找,分组查找等操做时变得异常简单。而 B 树因为数据分散在各个节点,要实现这些操做很不容易。

因为索引数据是按顺序排序的,即每次读取了数据页的时候,里面的索引数据大部分都是须要用的,因此也很好的解决了上文提到的如何存储尽可能多的有效的索引数据的问题。

总结

经过上面的分析,咱们能够发现,在使用某种解决方案的时候,这种方案必定是用来知足某些需求的,在知足需求的过程当中就会遇到一些问题,而最终的解决方案必定是能尽可能好的解决问题并知足需求的。

因此,探究清楚某种方案是要知足什么样的需求,解决什么样的问题以及如何的解决了问题,也就明白了为何使用这个方案。

更多好文,关注公众号获取

file
相关文章
相关标签/搜索