​MySQL索引的底层数据结构及算法

MySQL索引深度解析:B+树与数据结构优化,
本文详细介绍了SQL优化中索引的作用,重点讲解了B+树和不同存储引擎如InnoDB、MyISAM中索引的结构、工作原理及优缺点,包括主键索引和联合索引的比较策略。

我们经常在涉及到SQL优化的时候,总是会想到加索引。那索引究竟是个什么玩意儿?

索引是帮助MySQL高效获取数据,并且排好序的数据结构。

我们知道MySQL可以采用的是B+树和hash来维护索引的,hash这个结构并不常用,它虽然能够通过hash算法很快的路由到对应的数据,但是对于排序,hash就显得很鸡肋了。

树形结构在一定程度上,都是采用的二分查找,所以查询的时间复杂度都很低。

但随之而来又面临一系列的问题,比如树的退化,以及随着数据的不断增加,树的高度越来越高,磁盘IO的次数越来越多,导致性能越来越低,查询的时间越来越不可控。

打个比方:

二叉树

在极端的情况下,二叉树会退化成链表,查询的时间复杂度也随之退化。且树的高度越来越高。

红黑树

它又叫二叉平衡树,虽然自旋和变色解决了退化成链表的问题,但在一定程度上,树的高度递增的问题还是没有得到解决。

B树

它相当于是二叉树的一个进化版本,变成了多叉树,那么单个结点存储的数据就变多了,随之树的高度也得到了控制。

但是B树有一个特点,它的每个结点都存储了对应行的data。那么我们可以试想一下,这个索引的内存大小会特别大,而MySQL的内存是有限的,我们单表在大多数情况下,都不止一两个索引,那如果每个索引树都去维护一份data,这对于MySQL将会是一笔巨大的开销, 且造成了资源浪费。

另一方面,如果我们要对索引字段排序,B树会采取一系列的递归遍历,性能大打折扣。

B+树

它相当于是B树的一个进化版本, 从之前的每个结点都存储data,变为只有叶子结点存储data,且这个树的所有数据其实都在叶子结点上。

并且B+树在叶子结点维护了一个单向指针(MySQL做了优化,变为了双向指针),这个指针在很大程度上解决了我们对索引字段排序的问题。

到这里,想必应该更加理解了“索引就是一个排好序的数据结构”。

PS:MySQL索引树的单个结点默认可以存储16K大小的数据,即这16K就是一个磁盘块。

意味着一个磁盘块能存储大量的索引字段,从而树的高度就得到了控制,而操作系统又有一个预读取的功能,所以每次读取索引树的时候其实就是读取一个磁盘块,把某个磁盘块的数据加载到内存中进行操作。

好了,索引的数据结构我们搞明白了,那接下来我们看一下MySQL是怎么通过这个索引树去进行快速查询的。

这里涉及到MySQL的存储引擎,像Innodb、MyISAM、 Memorry,我们就聊聊常见的Innodb和MyISAM。

PS:索引的数据结构维护在MySQL安装目录下的/data文件夹里面。

MyISAM

索引树和数据文件是分离的(非聚集),索引树中的data维护的只是对应数据文件的内存地址,通过内存地址找到对应的数据。

Innodb

它分为两种情况,一种是主键索引树(或者是唯一索引树),另一种是普通索引树(非聚集)。

主键索引树中的data维护的是对应行的数据,而普通索引树中维护的是对应行的主键id,通过这个主键id,去主键索引树中进行回表查询,从而找到对应的数据。

图片

PS:大家猜想一下,MySQL的联合索引是怎么去比较大小从而构成树的呢?

打个比方,比如(name、age)这两个字段构成一个联合索引,那么会先根据name的ASCII码比较大小,如果通过name就已经确定出大小了,则不用继续去比较age的大小了。

只有当name的ASCII码相等的时候,才用去比较age的大小。

最后:下方这份完整的软件测试视频教程已经整理上传完成,需要的朋友们可以自行领取【保证100%免费】

软件测试面试文档

我们学习必然是为了找到高薪的工作,下面这些面试题是来自阿里、腾讯、字节等一线互联网大厂最新的面试资料,并且有字节大佬给出了权威的解答,刷完这一套面试资料相信大家都能找到满意的工作。

<think>嗯,用户想了解MySQL索引背后的数据结构及其实现算法原理。这个问题很专业,需要从存储引擎层面展开回答,因为MySQL不同存储引擎使用的索引结构不同。 用户可能正在做数据库性能优化,或者在学习数据库底层原理。从问题表述看,用户应该有一定数据库基础,但可能对实现细节不太清楚。需要区分InnoDB和MyISAM这两个最常用引擎的差异。 先梳理核心知识点:B+树作为主流索引结构的原因需要重点解释,包括与B树的对比。哈希索引的适用场景也要说明,毕竟Memory引擎确实在用。对于InnoDB的聚簇索引特性,要用具体例子说明数据存储方式,比如用户表的主键存放位置。 算法层面要分两部分:索引创建过程(比如B+树如何分裂)和查询过程(范围查询的叶子节点遍历)。InnoDB的自适应哈希索引是个很好的优化点,应该单独说明其触发机制。 突然想到用户可能混淆"索引实现"和"数据存储"概念,需要强调聚簇索引中数据行即叶子节点这个特性。MyISAM的分离存储作为对比案例很有必要,可以准备文件偏移量的例子。 索引维护的成本容易被忽视,得补充插入删除时的树平衡操作。最后算法示例部分,伪代码比Python更合适,避免语言特性干扰对本质的理解。 用户可能延伸的问题包括:为什么不用红黑树?联合索引的最左前缀原理如何体现?这些潜在问题可以放在最后的提问环节。不过当前回答已经涉及B+树高度计算,应该能自然引出分页优化的思考。</think>MySQL索引的核心数据结构是**B+树**,辅以哈希索引等特定场景的优化结构。以下是详细原理分析: --- ### **一、核心数据结构:B+树** #### **1. 结构特性** - **多叉平衡树**:节点可存储多个键值(默认页大小16KB),树高度低(通常3~4层可存百万级数据)。 - **数据全在叶子节点**:非叶子节点仅存**键值+指针**(指向子节点),叶子节点包含**完整数据**(或主键值)。 - **叶子节点双向链表**:所有叶子节点通过指针顺序链接,支持高效范围查询(如`WHERE id BETWEEN 100 AND 200`)。 #### **2. B+树 vs B树** | **特性** | **B+树** | **B树** | |------------------|-----------------------------|----------------------| | 数据存储位置 | 仅叶子节点 | 所有节点均可存储数据 | | 叶子节点链接 | 双向链表 | 无链接 | | 范围查询效率 | $O(\log n + k)$(k为结果数)| $O(\log n \cdot k)$ | | 非叶子节点冗余 | 键值重复存储(加速遍历) | 无冗余 | #### **3. InnoDB实现细节** - **聚簇索引(Clustered Index)**: 表数据按主键顺序存储在B+树叶子节点中,数据行与主键绑定。 例如用户表`users(id PK, name)`,叶子节点结构: ```plaintext | 页1 | → | 页2 | → | 页3 | ...(双向链表) 每页存储:[(id=1, name="Alice"), (id=2, name="Bob"), ...] ``` - **二级索引(Secondary Index)**: 叶子节点存储**主键值**而非数据行物理地址。查询过程: ```sql SELECT * FROM users WHERE name = 'Bob'; ``` 1. 在`name`索引B+树中找到`'Bob'`对应的主键值(如id=2) 2. 用主键值回表查询聚簇索引获取完整数据(*回表操作*) --- ### **二、其他索引类型** #### **1. 哈希索引** - **实现方式**: InnoDB自适应哈希索引(Adaptive Hash Index),由系统自动管理,用户不可控。 - **触发条件**:频繁访问某索引值且模式固定(如`WHERE id = 100`)。 - **数据结构**: 维护一个哈希表,键=索引值,值=数据行位置(非叶子节点指针)。 - **局限性**: 不支持范围查询(如`id > 100`),仅适用于等值查询。 #### **2. 全文索引(FULLTEXT)** - **数据结构**:倒排索引(Inverted Index) 存储`单词 → 文档ID列表`的映射,例如: ``` "database" → [doc1, doc3, doc5] "index" → [doc2, doc4] ``` - **算法**:分词 + BM25/ TF-IDF 相关性排序。 #### **3. 空间索引(R-Tree)** - 用于地理数据(如`GEOMETRY`类型),实现原理为R-Tree(递归分割空间)。 --- ### **三、B+树操作算法** #### **1. 查询过程** - **等值查询**(如`id=100`): 从根节点二分查找,逐层向下,时间复杂度$O(\log_d n)$(d为节点分支因子)。 - **范围查询**(如`id BETWEEN 50 AND 200`): 1. 定位到50的叶子节点 2. 沿链表向后遍历至200 3. 时间复杂度$O(\log_d n + k)$(k为结果数量) #### **2. 插入操作** ```plaintext 1. 定位到目标叶子节点 2. 若节点未满:直接插入有序位置 3. 若节点已满:分裂节点(新节点复制一半数据) - 父节点添加新键值(指向新节点) - 递归检查父节点是否需分裂(可能引发树增高) ``` *示例:插入键值27导致节点分裂* ```mermaid graph TD A[根节点: [20, 40]] --> B[叶子1: 10,20] A --> C[叶子2: 30,40] 插入27 → C[叶子2: 27,30,40] → 分裂为C1[27]和C2[30,40] 更新根节点为[20,27,40] ``` #### **3. 删除操作** - 直接删除叶子节点数据 - 若节点元素少于填充因子(默认50%),尝试合并相邻节点 --- ### **四、性能优化机制** 1. **页压缩(Page Compression)** 对索引页应用字典压缩(如`zlib`),减少I/O开销。 2. **Change Buffer** 对非唯一索引的插入/删除操作缓存,延迟写入磁盘(减少随机I/O)。 3. **覆盖索引(Covering Index)** 若查询字段全在索引中,避免回表: ```sql SELECT name FROM users WHERE age > 30; -- 若索引为(age,name)则无需回表 ``` --- ### **五、不同存储引擎对比** | **引擎** | 索引类型 | 数据存储方式 | |----------|------------------|--------------------------| | InnoDB | 聚簇索引+B+树 | 数据行在叶子节点 | | MyISAM | 非聚簇索引+B+树 | 数据与索引分离(.MYD/.MYI)| | Memory | 默认哈希索引 | 堆表 | > **关键结论**:B+树的低高度、顺序访问特性,使其成为磁盘数据库索引的最优解[^1]。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值