B+ 树是数据库索引最常用、最高效的数据结构之一,它是在 B 树基础上优化而来的。理解其结构和原理,以及为什么它比 B 树更适合数据库,关键在于其设计如何针对磁盘存储和数据库查询模式进行了优化。

一、 B+ 树的结构与原理

  1. 核心特征:

    • 多路平衡搜索树: 和 B 树一样,每个节点可以有多个子节点(称为“阶”或“度”,记为 m),这显著降低了树的高度。
    • 所有数据记录存储在叶子节点: 这是与 B 树最本质的区别。非叶子节点(内部节点)仅存储键(Key) 和指向子节点的指针。这些键充当路由信息,用于在树中导航。
    • 叶子节点包含所有键: 叶子节点不仅存储实际的数据记录(或指向数据记录的指针),还存储了对应的键,并且这些键是按顺序排列的。
    • 叶子节点通过指针串联成有序链表: 所有叶子节点通过双向(或单向)指针连接起来,形成一个按键值排序的有序链表。这是 B+ 树实现高效范围查询的关键。
    • 树的高度平衡: 插入和删除操作会遵循严格的规则(如节点分裂、合并、借键),保证从根节点到任意叶子节点的路径长度都相同(所有叶子节点都在同一层),确保操作效率的稳定性(O(log n))。
    • 节点填充因子: 通常要求节点(除了根节点)的键数量至少达到 ceil(m/2) - 1,最多为 m - 1(有时定义略有差异,但核心是控制最小填充度)。这保证了空间利用率和树结构的紧凑性。
  2. 工作原理:

    • 查找:

      1. 从根节点开始。
      2. 在当前节点中找到第一个大于或等于目标键的键(通过顺序扫描或二分查找)。
      3. 根据该键对应的指针(或小于该键的指针)进入相应的子节点。
      4. 重复步骤 2-3,直到到达叶子节点。
      5. 在叶子节点中顺序扫描(或二分查找)找到目标键。
      6. 如果找到,则获取键关联的数据记录(或指针);如果没找到,则记录不存在。
    • 插入:
      1. 按照查找的路径定位到目标键应该插入的叶子节点。
      2. 将键(以及对应的数据记录/指针)按顺序插入到该叶子节点。
      3. 如果插入后叶子节点键数超过上限 m-1,则进行节点分裂
        • 将该节点分裂成两个节点(通常是均分)。
        • 将分裂后新节点的最小键(或第一个键)复制到父节点中(作为新的分隔键),并添加指向新节点的指针。
        • 如果父节点也因此溢出,则递归向上分裂,可能最终导致树的高度增加。
      4. 如果根节点分裂,会创建一个新的根节点。
    • 删除:
      1. 按照查找的路径定位到包含目标键的叶子节点。
      2. 从叶子节点中删除该键及其关联的数据记录/指针。
      3. 如果删除后叶子节点的键数低于下限 ceil(m/2) - 1
        • 尝试借键: 检查相邻的兄弟节点(左或右)。如果某个兄弟节点有富余的键(> ceil(m/2) - 1),则从父节点借一个分隔键下来,并从兄弟节点移一个键(及相应指针)过来,同时更新父节点的分隔键。
        • 节点合并: 如果兄弟节点也没有富余键,则将该节点、一个兄弟节点以及父节点中它们之间的分隔键合并成一个新节点(或直接合并到兄弟节点)。删除父节点中的分隔键。
      4. 合并操作可能导致父节点下溢,需要递归向上进行借键或合并操作,可能最终导致树的高度降低。
    • 范围查询:
      1. 通过查找操作定位到范围起始键所在的叶子节点。
      2. 读取该叶子节点上所有满足范围的记录。
      3. 沿着叶子节点的链表指针(通常是向右)遍历后续叶子节点,读取并筛选记录,直到遇到超出范围的键。

二、 为什么 B+ 树比 B 树更适合数据库索引?

B+ 树的设计在以下几个方面针对数据库(尤其是基于磁盘的系统)进行了优化,使其相比 B 树具有显著优势:

  1. 更高的扇出,更低的树高:

    • 由于非叶子节点只存储键和指针,不存储数据记录,所以一个非叶子节点可以容纳更多的键(键比数据记录小得多)。
    • 更高的扇出(一个节点能指向的子节点数)意味着对于相同数量的数据记录,B+ 树的高度通常比 B 树更低
    • 意义: 更低的树高意味着查找、插入、删除操作需要访问的磁盘 I/O 次数更少。磁盘 I/O 是数据库操作中最耗时的部分,减少 I/O 是提升性能的关键。即使数据量巨大,B+ 树也能保持较少的层级访问。
  2. 更稳定的查询性能(所有查询都到叶子节点):

    • 在 B+ 树中,任何查询(精确查找、范围查找)都必须遍历到叶子节点才能找到数据。无论键在树中何处出现(可能在非叶子节点出现多次),数据只在叶子节点。
    • 在 B 树中,数据记录可能存储在任何节点(非叶子或叶子)。这意味着精确查找可能在非叶子节点就找到结果并提前返回。
    • 意义: B+ 树的查询路径长度总是等于树高,非常稳定和可预测(O(h))。B 树的查询路径长度则可能小于树高(提前找到),但波动性较大。对于数据库系统来说,稳定和可预测的性能非常重要,尤其是在高并发和复杂查询场景下。
  3. 无与伦比的范围查询效率:

    • 叶子节点间的有序链表是 B+ 树的核心优势之一。
    • 执行范围查询(如 SELECT * FROM table WHERE key BETWEEN 10 AND 100)时:
      • B+ 树:找到起始键 (10) 所在的叶子节点后,只需顺序扫描该节点和链表连接的后续叶子节点即可高效获取所有范围内的记录。这最大限度地利用了磁盘的顺序读取特性(远快于随机读取)。
      • B 树:没有叶子链表。找到起始键后,要获取后续记录,必须不断地回溯到父节点,再定位到下一个子节点(可能在不同的磁盘页),进行中序遍历。这会产生大量的随机磁盘 I/O,性能远低于 B+ 树的顺序扫描。
    • 意义: 范围查询是数据库中最常见、最重要的操作之一(如按时间范围筛选、分页查询)。B+ 树对此类查询的优化是革命性的。
  4. 更少的空间占用(非叶子节点):

    • 非叶子节点不存储实际数据,只存储键和指针,通常比存储完整数据记录的 B 树非叶子节点小得多
    • 意义:
      • 更多的非叶子节点可以缓存在宝贵的内存中(Buffer Pool),进一步减少磁盘 I/O。
      • 即使需要从磁盘读取非叶子节点,更小的节点意味着一次 I/O 可以读取更多的路由信息(键和指针),间接提升了扇出和降低了树高。
  5. 全表扫描更高效:

    • 如果需要对整个表进行扫描(如 SELECT * FROM table,无 WHERE 条件),B+ 树只需遍历叶子节点的链表即可顺序访问所有记录。
    • B 树进行全表扫描也需要进行树的中序遍历(递归或栈),效率低于顺序扫描链表。

总结对比表

特性 B+ 树 B 树 对数据库的意义
数据存储位置 仅在叶子节点 所有节点(叶子 + 非叶子)都可能存储数据 B+ 树非叶节点更小,扇出更高
非叶子节点内容 仅键 + 指针(路由信息) 键 + 指针 + 可能的数据记录 B+ 树非叶节点更小,扇出更高
叶子节点连接 通过指针形成有序链表 无显式链表连接 B+ 树范围查询高效(顺序 I/O)
查找性能稳定性 稳定 (总是到叶子节点,路径长=树高) 不稳定 (可能中途找到,路径长 <= 树高) B+ 树性能可预测性更好
范围查询效率 极高 (顺序遍历叶子链表) 较低 (需中序遍历,随机 I/O 多) B+ 树更适合常见数据库操作 (BETWEEN, >, <)
等值查询 I/O 次数 通常更少 (树高更低) 可能更少或更多 (树高可能更高) B+ 树平均 I/O 更少
全表扫描效率 (顺序遍历叶子链表) (中序遍历) B+ 树更高效
非叶子节点空间占用 更小 (只存键+指针) 更大 (可能存数据) B+ 树缓存更有效,间接提升 I/O

结论:

B+ 树通过将数据集中存储在叶子节点用链表连接叶子节点的核心设计,完美适配了数据库系统的主要需求:减少昂贵的磁盘 I/O 次数(尤其是通过更高的扇出降低树高)和高效支持范围查询(通过叶子链表实现顺序访问)。虽然精确查找在 B 树中有时可能更快(提前返回),但这种优势在数据库庞大的数据量和频繁的范围查询面前显得微不足道。B+ 树在稳定性、整体性能(特别是范围查询)和磁盘 I/O 优化方面的综合优势,使其成为数据库索引事实上的标准结构。几乎所有主流的关系型数据库(如 MySQL InnoDB, PostgreSQL, Oracle, SQL Server)以及许多 NoSQL 数据库都使用 B+ 树或其变种作为主要的索引实现方式。

【数据库索引标准结构】B+树原理详解与B树对比优势的更多相关文章

  1. 硬盘内部硬件结构和工作原理详解[zz]

    一般硬盘正面贴有产品标签,主要包括厂家信息和产品信息,如商标.型号.序列号.生产日期.容量.参数和主从设置方法等.这些信息是正确使用硬盘的基本依据,下面将逐步介绍它们的含义. 硬盘主要由盘体.控制电路 ...

  2. 0614MySQL的InnoDB索引原理详解

    转自http://www.cnblogs.com/shijingxiang/articles/4743324.html MySQL的InnoDB索引原理详解 http://www.admin10000 ...

  3. Influxdb原理详解

    本文属于<InfluxDB系列教程>文章系列,该系列共包括以下 15 部分: InfluxDB学习之InfluxDB的安装和简介 InfluxDB学习之InfluxDB的基本概念 Infl ...

  4. VLAN原理详解[转载] 网桥--交换机---路由器

    来自:http://blog.csdn.net/phunxm/article/details/9498829 一.什么是桥接          桥接工作在OSI网络参考模型的第二层数据链路层,是一种以 ...

  5. 节点地址的函数list_entry()原理详解

    本节中,我们继续讲解,在linux2.4内核下,如果通过一些列函数从路径名找到目标节点. 3.3.1)接下来查看chached_lookup()的代码(namei.c) [path_walk()> ...

  6. 【转】VLAN原理详解

    1.为什么需要VLAN 1.1 什么是VLAN? VLAN(Virtual LAN),翻译成中文是“虚拟局域网”.LAN可以是由少数几台家用计算机构成的网络,也可以是数以百计的计算机构成的企业网络.V ...

  7. TOMCAT原理详解及请求过程(转载)

    转自https://www.cnblogs.com/hggen/p/6264475.html TOMCAT原理详解及请求过程 Tomcat: Tomcat是一个JSP/Servlet容器.其作为Ser ...

  8. [No0000126]SSL/TLS原理详解与WCF中的WS-Security

    SSL/TLS作为一种互联网安全加密技术 1. SSL/TLS概览 1.1 整体结构 SSL是一个介于HTTP协议与TCP之间的一个可选层,其位置大致如下: SSL:(Secure Socket La ...

  9. LVS原理详解(3种工作模式及8种调度算法)

    2017年1月12日, 星期四 LVS原理详解(3种工作模式及8种调度算法)   LVS原理详解及部署之二:LVS原理详解(3种工作方式8种调度算法) 作者:woshiliwentong  发布日期: ...

  10. Storm概念、原理详解及其应用(一)BaseStorm

    本文借鉴官文,添加了一些解释和看法,其中有些理解,写的比较粗糙,有问题的地方希望大家指出.写这篇文章,是想把一些官文和资料中基础.重点拿出来,能总结出便于大家理解的话语.与大多数“wordcount” ...

随机推荐

  1. 操作系统 -- 第一个C函数

    前面我们为调用Cosmos 的第一个C函数hal_start做了大量的工作,本节要让操作系统Cosmos里的第一个C语言真正跑起来. 继续在hal_start函数里,首先执行板级初始化,其实就是hal ...

  2. WPF竖向排列的按钮

    <Button Width="100" Height="150"> <Button.Content> <TextBlock Tex ...

  3. WPF placeHolder效果的样式设定

    <UserControl x:Class="wpfTestStudio.textPlaceHolderDemo" xmlns="http://schemas.mic ...

  4. 基于.NetCore开发 StarBlog 番外篇 (4) 文章一键发布工具Publisher大升级,AI功能增强与界面优化

    前言 自从上次开发了 StarBlogPublisher 这个文章创作神器之后 我的博客+公众号文章工作流效率提升了不少 不过这软件还有一些功能欠缺和我不满意的地方 这次就在这个下暴雨的周末,把这个软 ...

  5. Github如何创建添加开源许可license

    我们点击项目上方的 Add file,选择 creat new file 名称填写 LICENSE, 右侧便会出现按钮 Choose a license template 选择你要的证书,填写 年份 ...

  6. http协议中长连接和短连接介绍

      TCP连接 当网络通信时采用TCP协议时,在真正的读写操作之前,客户端与服务器端之间必须建立一个连接,当读写操作完成后,双方不再需要这个连接时可以释放这个连接.连接的建立依靠"三次握手& ...

  7. 「Note」数据结构方向 - 数据结构进阶

    1. 平衡树(FHQ-Treap) 1.1. 介绍 功能强大的平衡树,以至于我根本没学 Treap 以及 Splay(LCT 里的另谈),缺点就大概是常数大. FHQ-Treap 核心操作在于分裂与合 ...

  8. k8s gpu共享

    k8s gpu 共享方案 1.配置环境变量实现 通过在POD的启动文件中,配置NVIDIA_VISIBLE_DEVICES的环境变量,指定显卡号.NVIDIA_VISIBLE_DEVICES可配置为具 ...

  9. 数栈干货放送!babel-plugin-import最全源码详解

    ​ 本文将带领大家解析babel-plugin-import 实现按需加载的完整流程,解开业界所认可 babel 插件的面纱. 首先供上babel-plugin-import插件 一.初见萌芽 首先 ...

  10. 流批一体开源项目ChunJun技术公开课——ChunJun同步Hive事务表

    一键直达直播间 一.直播介绍 上两期渡劫同学为大家分享了ChunJun数据还原的DDL模块,想必大家对这一模块有了比较深入的了解,本期无倦同学将会为大家分享ChunJun同步Hive事务表的相关内容, ...