图解对比MySQL索引为什么要用B+树

本文围绕MySQL索引展开,指出索引设计对程序性能至关重要。介绍了MySQL常见的索引类型,重点讲解B+树索引及其作为索引数据结构的原因。还重温了哈希结构、二叉查找树、平衡二叉树和B+树等数据结构,对比了它们在直接查询和范围查询上的性能。

讲到索引,第一反应肯定是能提高查询效率。例如书的目录,想要查找某一章节,会先从目录中定位。如果没有目录,那么就需要将所有内容都看一遍才能找到。

索引的设计对程序的性能至关重要,若索引太少,对查询性能受影响;而如果索引太多,则会影响增/改/删等的性能。

知识点

MySQL中一般支持以下几种常见的索引:

  • B+树索引
  • 全文索引
  • 哈希索引

我们今天重点来讲下B+树索引,以及为什么要用B+树来作为索引的数据结构。

B+树索引并不能直接找到具体的行,只是找到被查找行所在的页,然后DB通过把整页读入内存,再在内存中查找。

重温数据结构

1.1 哈希结构

如有 3、1、2、10、9、0、4、6这8个数据,建立如 图1-1所示哈希索引。

  • 直接查询:现在要从8个数中查找6这条记录,只需要计算6的哈希值,便可快速定位记录,时间复杂度为O(1)。
  • 范围查询:如果要进行范围查询(大于4的数据),那这个索引就完全没用了。
图解对比MySQL索引为什么要用B+树

1.2 二叉树查找树

二叉树是一种经典的数据结构,要求左子树小于根节点,右子树大于根节点。

如有 3、1、2、10、9、0、4、6这8个数据,建立如 图1-2所示二分查找树。

  • 直接查询:假设查找键值为6的记录,先找到根4,4<6,因此查找4的右子树,找到9;9大于6,因此查找9的左子树;一共查找3次。但如果顺序查找,则需要查找8次(位于最后)。
  • 范围查询:如果需要查找大于4的数据,则遍历4的右子树就行了
图解对比MySQL索引为什么要用B+树

 

1.3 平衡二叉树(AVL树)

按照二叉查找树的定义,它是可以任意的构造,同样是这些数字,可以按照 图1-3-1的方式来建立二叉查找树。同样查找数据6,需要查找5次。

图解对比MySQL索引为什么要用B+树

图1-3-1 性能较差的二叉查找树

因此为了最大性能地构造一个二叉查找树,需要它是平衡的,即平衡二叉树。

平衡二叉树定义:首先符合二叉查找树的定义,另外任何节点的两个子树高度最大差为1。

平衡二叉树的查询速度是很快的,但是有缺点:

  1. 维护树的代价是非常大,在进行插入或更新时,经常会需要多次左旋或右旋来维持平衡。如 图1-3-2所示
  2. 数据量多的时候,树会很高,需要多次I/O操作。
  3. 在进行范围查找时,假设查找>=3,先找到3,然后需要查找到3的父节点,然后遍历父节点的右子树。

图解对比MySQL索引为什么要用B+树

 

1.4 B+ 树

在B+树中,所有记录节点存放在叶子节点上,且是顺序存放,由各叶子节点指针进行连接。如果从最左边的叶子节点开始顺序遍历,能得到所有键值的顺序排序。

如有 3、1、2、10、9、0、4、6这8个数据,可建立如图1-4-1所示高度为2的B+树。

 

图解对比MySQL索引为什么要用B+树

 图1-4-1 高度为2的B+树

 

在进行更新时,B+树同样需要类似二叉树的旋转操作。举例,假设新增一个7,那可以直接填充到4、6的后面。如果再添加8,那么就需要进行旋转了,感受下面的B+树旋转过程。

 

图解对比MySQL索引为什么要用B+树

图1-4-2 高度为3的B+树

 

采用B+树的索引结构优点:

  1. B+树的高度一般为2-4层,所以查找记录时最多只需要2-4次IO,相对二叉平衡树已经大大降低了。
  2. 范围查找时,能通过叶子节点的指针获取数据。例如查找大于等于3的数据,当在叶子节点中查到3时,通过3的尾指针便能获取所有数据,而不需要再像二叉树一样再获取到3的父节点。

看到这应该明白mysql索引为什么使用B+树了吧

转载于:https://www.cnblogs.com/CQqf2019/p/10929449.html

<think>我们正在讨论MySQL索引原理,特别是B+结构。用户要求通过图片详细解释,但在文本环境中,我将用文字描述B+结构,并尽量详细图解。同时,用户提到Linux环境,但索引机制在MySQL操作系统关系不大,主要是在存储引擎实现(如InnoDB)。根据引用[2][4],B+是B-的变体,广泛用于数据库和文件系统。在MySQL中,InnoDB引擎使用B+作为索引结构。###B+结构详解####1. B+的特点-所有数据都存储在叶子节点,内部节点只存储键值(作为索引)。 -叶子节点之间通过指针连接,形成有序链表。-一个节点可以包含多个键值,节点的大小通常设置为一个页(如InnoDB中默认为16KB),这样可以减少磁盘I/O次数。 ####2. B+结构图解(文字描述)假设我们有一个简单的B+,阶数(degree)为3(即每个节点最多有3个键值,4个孩子节点)。下面是一个示例: ```[10,20](内部节点)/|\/|\[5,8][15,18][25,30](内部节点)/|\/|\/|\/|\/|\/|\[1,3] [6,7] [9][12,14][16,17][19][22,24][26,28][31,33](叶子节点)->->-> ->-> ->-> ->-> ->-> ->-> ->-> ->-> ->(叶子节点之间的链表指针)``` -内部节点(非叶子节点):存储键值,用于路由。例如,根节点存储[10,20],表示小于10的键在左子,10到20之间的在中间子,大于20的在右子。 -叶子节点:存储实际的数据(在数据库中,这里存储的是数据行或者指向数据行的指针)。叶子节点之间通过指针连接,形成一个双向链表(图中用箭头表示),这样便于范围查询。####3.在MySQLInnoDB中的具体实现- InnoDB中,B+的叶子节点存储的是完整的数据行(对于主键索引)或者是主键值(对于二级索引)。-每个节点的大小为一个页(16KB),这样一次磁盘I/O可以读取一个完整的节点。 -在Linux环境下,MySQL通过文件系统(如Ext4)来管理数据文件。索引结构存储在.ibd文件中(对于独立表空间)或ibdata文件中(对于共享表空间)。 ####4. B+的优势(B相比) -由于内部节点不存储数据,所以可以存储更多的键值,从而降低的高度,减少磁盘I/O次数。-叶子节点形成有序链表,范围查询非常高效(只需要找到起始点,然后沿着链表遍历即可)。 ####5.查询过程以查询键值15为例:1.从根节点开始,15在[10,20]区间,进入中间子(节点[15,18])。 2.在节点[15,18]中,15等于第一个键,所以进入第一个孩子节点(实际实现中可能会根据具体算法选择,但大致如此)。 3.到达叶子节点[12,14]和[16,17]之间,实际上在节点[15,18]的孩子节点中,我们找到包含15的叶子节点(可能是[12,14,15]这样的节点,这里简化为[15]所在的叶子节点)。 4.在叶子节点中找到15对应的数据。###总结MySQL(InnoDB)使用B+作为索引结构,在Linux环境下,索引数据存储在表空间文件中。B+通过多路平衡和叶子节点链表设计,提供了高效的点查询和范围查询能力。由于文本限制,无法提供实际图片,但以上文字描述可以清晰展示B+的结构。建议用户搜索“B+结构图”以获取直观图片。 §§相关问题§§1. InnoDB中B+的节点大小为什么是16KB?可以调整吗? 2. B+在插入和删除时如何保持平衡?3.二级索引(非主键索引)在B+中是如何存储的? 4.在Linux文件系统中,MySQL如何利用B+减少磁盘I/O?</think>### MySQL索引原理B+结构详解(Linux环境) MySQL在Linux环境下使用**B+**作为索引的核心数据结构,尤其在InnoDB存储引擎中。以下是详细解析: --- #### **一、B+核心结构图解(文字描述)** 假设一个阶数$d=3$的B+(每个节点最多容纳$2d=6$个键值),结构示意如下: ``` [根节点] | +-------+-------+-------+ | 10 (指针) | 20 (指针) | 30 (指针) | +-------+-------+-------+ / | \ / | \ +----------------+ +-------------+ +----------------+ |5,8,9| | |15,18| | |25,28| | ← 内部节点 +------+---------+ +------+-------+ +------+--------+ / | \ | \ | \ / | \ | \ | \ +-----+ +-----+ +-----+ +-----+ +-----+ +-----+ +-----+ |1,2,3|->|5,6,7|->|8,9 |->|15,17|->|18,19|->|25,26|->|28,30| ← 叶子节点 +-----+ +-----+ +-----+ +-----+ +-----+ +-----+ +-----+ (数据指针) (数据指针) ... (数据指针) ... (数据指针) ... ↓链表指针→↓链表指针→↓... ↓链表指针→↓... ↓链表指针→↓... ``` **关键组件说明**: 1. **内部节点(非叶子节点)** - 仅存储**键值**和**子节点指针**(不存实际数据) - 键值数量范围:$[d, 2d]$($d$为阶数) - 作用:路由导航(如查找`15`:根节点→`10<15<20`→中间子2. **叶子节点** - 存储**键值**和**数据指针**(指向实际行数据) - 所有叶子节点通过**双向链表**串联(图中`→`箭头),支持高效范围查询 - 数据指针在InnoDB中为主键值(聚簇索引)或磁盘地址(二级索引) 3. **Linux环境下的存储实现** - 每个节点大小 = **16KB**(InnoDB默认页大小) - 索引文件存储路径:`/var/lib/mysql/数据库名/表名.ibd` - 磁盘读写以**页为单位**(减少I/O次数) --- #### **二、B+MySQL中的优势** 1. **低高 & 高效I/O** - 典型场景:千万级数据高仅$3 \sim 4$ - 单次查询只需$1 \sim 3$次磁盘I/O(Linux内核优化顺序读取) 2. **范围查询优化** 叶子节点链表支持直接遍历(如`WHERE id BETWEEN 15 AND 25`) 3. **聚簇索引 vs 二级索引** | **索引类型** | **叶子节点存储内容** | **Linux文件示例** | |--------------|----------------------------|-------------------------| | 聚簇索引 | 完整行数据 | `表名.ibd`中的主键区块 | | 二级索引 | 主键值 + 索引列值 | 同一文件的二级索引区块 | --- #### **三、Linux环境下B+操作流程** 1. **查询键值`18`的流程**: ```mermaid graph LR A[访问根节点页] --> B{18>10?} B -->|是| C[加载中间子页] C --> D{18<20?} D -->|是| E[加载叶子节点页] E --> F[找到键18的数据指针] F --> G[从.ibd文件读取行数据] ``` 2. **插入键`16`的平衡操作**: - 目标叶子节点`[15,17]`已满 → 分裂为`[15,16]`和`[17]` - 向上递归更新父节点(若父节点满则继续分裂) - Linux写操作:脏页通过`fsync()`刷盘[^2] --- #### **四、性能对比(B+ vs B)** | **特性** | **B+** | **B** | |----------------|---------------------------|-----------------------| | 数据存储位置 | 仅叶子节点 | 所有节点 | | 范围查询 | $O(1)$(链表遍历) | $O(\log n)$ | | 高 | 更低(内部节点无数据) | 略高 | | Linux磁盘I/O | 更少(页缓存利用率高) | 较多 | > **注**:Linux的Page Cache机制会缓存频繁访问的索引页(`/proc/meminfo`可监控)。 --- #### **五、总结** MySQL在Linux中通过B+实现索引的核心价值: 1. **结构特性**:多路平衡 + 叶子链表 + 数据集中于叶子 2. **Linux优化**:页缓存机制 + 块设备I/O调度(如CFQ/Deadline) 3. **适用场景**:高频范围查询(如日志分析)、高并发点查(如电商ID检索)
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值