树 Story —— B 树 / B+ 树

1,196 阅读6分钟

- 树 Story 第五篇 - 「B 树 / B+ 树」

本文详细阐述了多路查找树原理,适合新手阅读,以及老手回顾。 全文两千字,阅读时间 10 分钟。

用过 MySQL 的朋友一定对 B+ 树不陌生,MySQL 的索引结构就是 B+ 树。 B+ 树的概念是在 B 树之上,而 B 树是什么呢?

在计算机科学中,B树(英语:B-tree)是一种自平衡的树,能够保持数据有序。这种数据结构能够让查找数据、顺序访问、插入数据及删除的动作,都在对数时间内完成。B树,概括来说是一个一般化的二叉查找树(binary search tree),可以拥有多于2个子节点。与自平衡二叉查找树不同,B树为系统大块数据的读写操作做了优化。B树减少定位记录时所经历的中间过程,从而加快存取速度。B树这种数据结构可以用来描述外部存储。这种数据结构常被应用在数据库和文件系统的实现上。—— 百度百科

注: B-树等同于 B树,而不是 B 减树

B 树与 B+ 树

B 树与 B+ 树的主要区别有:

  1. B 树的数据(或指向数据的指针)存在每个节点里,而 B+ 树的数据(或指向数据的指针)仅存在叶子节点里,非叶子节点只有索引。
    • B 树查找可能会在任意一个节点停止
    • B+ 树的非叶子节点可以存储更多的索引值,阶数更高
  2. B+ 树的叶子节点使用双向链表链接,提高顺序查询效率
    • 相比于 B 树, B+ 树在区间查找方面更胜一筹

B 树更像是多路查找树,每个节点不仅包括索引,还包括了具体的数据(或指向数据的指针)

B+ 树的叶子节点才有数据(或指向数据的指针),而非叶子节点只有索引。并且叶子节点有双向链表链接。

MySQL 中的索引不管是 MyISAM 还是 InnoDB , 底层都是 B+ 树。而 MyISAM 和 InnoDB 的区别在于:MyISAM 的主键索引和辅助索引,叶子节点都是指向数据的值的指针,数据的值单独存储。而 InnoDB 的主键索引叶子节点保存了数据的值,而不是指针,辅助索引的叶子节点除了有索引,还保存了数据对应的主键值。(文末有更多延伸内容阅读)

我们都知道 MySQL 使用了 B+ 树结构,而 B 树结构在哪个场景应用了呢?

答案是: MongoDB。

我们都知道, MongoDB 是 NoSQL 数据库。对于 NoSQL 数据库,对单 key 的读写效率相比于区间查询要求更高。

B+ 树由于数据都在叶子节点上,所以查询时间复杂度比较稳定在 O(logN),而 B 树的查询最快可以在 O(1)完成。同时 B 树的数据存在于任意节点上,所以数据的读取也比较方便。

而在非关系型数据库的索引结构里,还有一个树形结构是我们很少提及的—— LSM (Log Structure Merge),其在 HBase 里作为主要的设计思想, 下一篇文章我们将详细聊一聊 LSM 。

仅存在叶子节点里,非叶子节点只有索引

附:以下是针对 MyISAM 和 InnoDB 实现的阐述,摘抄于网络,感兴趣的读者可以阅读

MyISAM索引实现

1. 主键索引

MyISAM引擎使用B+树作为索引结果,叶节点的data域存放的是数据记录的地址。下图为MyISAM表的主索引,Col1为主键。

https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/57bc6754a5ec42bdbb84484c656acd9e~tplv-k3u1fbpfcp-zoom-1.image

2. 辅助索引

**在MyISAM中,主索引和辅助索引在结构上没有任何区别,只是主索引要求key是唯一的,而辅助索引的key可以重复。**下图在Col2上建立一个辅助索引

https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/d4d1f2ccd93f400fa4f56760e692c579~tplv-k3u1fbpfcp-zoom-1.image

同样也是一颗B+Tree,data域保存数据记录的地址。因此,MyISAM中索引检索的算法为首先按照B+Tree搜索算法搜索索引,如果指定的Key存在,则取出其data域的值,然后以data域的值为地址,读取相应数据记录。

MyISAM的索引方式也叫做“非聚集”的,之所以这么称呼是为了与InnoDB的聚集索引区分。

InnoDB索引实现

1 主键索引

同样是B+树,实现方式却完全不同。InnoDB表数据文件本身就是一个索引结构,树的叶节点data域保存了完整的数据记录,这种索引叫做聚集索引

https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/2d11682e7df248ed9dbd51442030a1f4~tplv-k3u1fbpfcp-zoom-1.image

因为InnoDB的数据文件本身要按主键聚集,所以InnoDB要求表必须有主键(MyISAM可以没有),如果没有显式指定,则mysql会自动选择一个可以唯一标识数据记录的列作为主键。如果不存在这种列,则mysql自动为InnoDB表生成一个隐含字段作为主键,这个字段长度为6个字节,类型为长整型。

2 辅助索引

InnoDB的所有辅助索引都引用主键作为data域。下图为定义在Col3上的一个辅助索引

https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/70b82b81ee8e4282b62950d088df958b~tplv-k3u1fbpfcp-zoom-1.image

因此InnoDB 的索引能提供一种非常快速的主键查找性能。不过,它的辅助索引也会包含主键列,所以如果主键定义的比较大,其他索引也将很大。InnoDB 不会压缩索引。

聚集索引这种实现方式使得按主键的搜索十分高效,但是辅助索引搜索需要检索两遍索引:首先检索辅助索引获得主键,然后用主键到主索引中检索获得记录。

不同存储引擎的索引实现方式对于正确使用和优化索引都非常有帮助,例如知道了InnoDB的索引实现后,就很容易明白为什么不建议使用过长的字段作为主键,因为所有辅助索引都引用主索引,过长的主索引会令辅助索引变得过大。再例如,用非单调的字段作为主键在InnoDB中不是个好主意,因为InnoDB数据文件本身是一颗B+Tree,非单调的主键会造成在插入新记录时数据文件为了维持B+Tree的特性而频繁的分裂调整,十分低效,而使用自增字段作为主键则是一个很好的选择。

摘抄部分来源:www.cnblogs.com/balfish/p/8…