文章目录
-
- 叶子节点、非叶子节点
- [解析 B+ 树、二叉查找树](#解析 B+ 树、二叉查找树)
- 一、先纠正误区:不是所有树都是"一个节点存一个值"
- 二、一个节点里到底存了什么?
叶子节点、非叶子节点
B+ 树,本质就是一棵从上到下分层的树状目录结构,一共就两类节点:
- 上面所有层 → 非叶子节点(目录层)
- 最底下一层 → 叶子节点(数据层)
一、非叶子节点:只当"指路牌",不存真实数据
是什么
就是树的中间目录层 ,
最顶部的叫根节点,中间的叫分支节点,它们统一都叫非叶子节点。
里面存什么
只存两样东西:
- 索引键:比如主键id的值(10、20、30...)
- 下一层节点的地址:相当于"页码",告诉你下一层的节点在磁盘的哪个位置
它里面半条真实的行数据都没有,比如,你查id=15的员工,非叶子节点里不会存这个员工的姓名、年龄这些信息,只会告诉你"15在第二个叶子节点里"。
作用:只做导航
就像字典的拼音目录、部首目录,它的唯一作用就是帮你快速定位到"数据在哪一页",本身不提供任何完整内容。
比如,你查 "张" 字:
- 先看声母目录,找到 zh 对应的页码范围
- 再翻到韵母目录,找到 ang 对应的具体页码
- 最后才翻到正文页
前面两步的目录,就全是非叶子节点,只指路,不存解释。
二、叶子节点:真正存数据的地方
是什么
是 B+树 最底下的一层,也是 查询的最终目的地。
里面存什么
在 InnoDB 的聚簇索引里:
- 叶子节点存的是完整的一行数据(id、name、age、phone、email ... 所有字段的真实值)
这就是那句话的意思:所有数据都存在叶子节点。你要的任何真实业务数据,只能在最底层的叶子节点里找到,上面的目录层里一概没有。
额外特点:有序且双向相连
所有叶子节点,按索引值从小到大排好序,并且,相邻的叶子节点之间用 "双向链表" 连起来。
好处就是:一旦你找到了起点,比如 id>100的第一条数据,顺着链表往后挨个走,就能把所有符合范围的数据都取出来,天然支持范围查询和排序。
三、用一张文字结构图直观感受
B+树是 多叉树(也叫多路搜索树) ,不是二叉树。
一个节点可以存很多个索引键 ,不是只能存1个。
例子里只画了2个id,是为了画图方便做的极度简化,实际一个节点里能存上千个id。
【根节点(非叶子)】
id: 20 id: 50
/ \
【分支节点】 【分支节点】(都是非叶子)
id:10 id:15 id:30 id:40
/ \ / \
【叶子1】 ↔ 【叶子2】 ↔ 【叶子3】 ↔ 【叶子4】(最底层,存完整数据)
id:1-9 id:10-19 id:20-29 id:30-49
存完整行 存完整行 存完整行 存完整行
你查 WHERE id = 17 的过程:
- 先到根节点:17 < 20,走左边分支
- 到分支节点:15 < 17 < 20,走第二个叶子节点
- 到叶子2节点,找到id=17的完整行数据,查询结束
上面两步走的都是非叶子节点,纯指路;最后一步到叶子节点,才拿到真实数据。
四、为什么这么设计,就能"IO少、速度快"?
这就是之前说的 "矮胖结构" 的核心原因:
- 非叶子节点不存数据,只存 键值 + 地址,体积非常小。MySQL一页默认16KB,一页就能存上千个主键值。
- 一层就能覆盖上千个范围,两层就能覆盖上百万条数据,三层就能覆盖上亿条数据。
- 所以百万级数据,查询最多也就读3次磁盘(根节点 → 分支节点 → 叶子节点),速度极快。
如果非叶子节点也存数据,那一页存不了几个键,树就会变得又高又瘦,查一条数据要读十几次磁盘,就慢了。
解析 B+ 树、二叉查找树
B+树是 多叉树(也叫多路搜索树) ,不是二叉树。
一个节点可以存很多个索引键,不是只能存1个。
一、先纠正误区:不是所有树都是"一个节点存一个值"
你会有 "一个节点存一个id" 的印象,大概率是之前见过二叉查找树:
- 二叉树:每个节点只能存1个值,最多分左右两个分叉。
- B+树:每个节点可以存N个值,分出N+1个分叉,所以叫"多叉树"。
这也是B+树能做到"矮胖"、IO次数少的根本原因。
二、一个节点里到底存了什么?
我们拿非叶子节点举例,它的内部结构是:「索引键」和「子节点指针」交替排列。
规则很简单:
- 如果一个节点里有 N 个索引键 ,就会对应 N+1 个子节点指针。
- 每个指针,负责指向一个数值区间。
就拿你图里的根节点举例:
根节点里的值:id: 20 id: 50
对应指针: ↙ ↓ ↘
指针1 指针2 指针3
三个指针对应三个区间:
- 指针1:所有 id < 20 的数据,去左边的子节点找
- 指针2:所有 20 ≤ id < 50 的数据,去中间的子节点找
- 指针3:所有 id ≥ 50 的数据,去右边的子节点找
上面那个图里只画了两个分支,是因为画图时把最右边大于50的部分省略了,属于简化示意。
为什么要这么设计?
一页目录能放下几十个分界标记,就能指引几十页正文,目录本身就不用做太厚。对应到数据库:
- 一个节点就是MySQL的一页数据(默认16KB),只存索引键+指针,能放下上千个id。
- 一层就能划分上千个区间,两层就能覆盖上百万条数据,三层就能覆盖上亿条。
- 树的高度只有3-4层,查一条数据最多读3次磁盘,这就是B+树快的核心。
如果一个节点只能存1个id(二叉树),那百万条数据树高就有20层,查一次要读20次磁盘,速度会慢很多。