当前位置: 首页 > news >正文

MySQL 索引底层 B+ 树原理

聊 MySQL 索引,不讲 B+ 树,那就是在耍流氓。

大家好,我是乱码字符。今天咱们深入聊聊 MySQL 索引的底层数据结构——B+ 树。这篇文章能让你彻底搞明白,为什么有时候明明加了索引,查询却还是慢成狗。

先说说为什么要用树结构

想象一下,如果数据存在数组里,你要查一条id = 10086的记录,最坏情况得遍历整个数组,时间复杂度 O(n)。数据少还行,一旦数据量级到了千万、亿这个级别,查询效率就炸了。

所以我们需要一种查找效率高的数据结构。哈希表?那玩意儿只能做等值查询,范围查询(比如查id > 100)就歇菜了。

树结构天然支持高效的范围查询,这就是 MySQL 选择 B+ 树的主要原因。

B+ 树长啥样?

先上一张经典的 B+ 树结构图(建议收藏):

[50 | 100] / | \ [20|30] [60|80] [120|150] / | \ / | \ / | \ ... ... ... ... ... ... ... ``` 简单说,B+ 树是一种**多叉平衡树**,有多牛呢?看几个关键特性: 1. **只有叶子节点存数据**——非叶子节点只存索引(键值),叶子节点才是真正存数据的地方 2. 2. **叶子节点用指针串起来**——这也就是为什么 B+ 树特别适合范围查询 3. 3. **绝对平衡**——所有叶子节点都在同一层,查询性能极其稳定 ## B+ 树 vs B 树,有啥区别? 面试官最喜欢问这个,记住了: | 特性 | B 树 | B+ 树 | |------|------|-------| | 数据存储 | 非叶子节点也存数据 | 只有叶子节点存数据 | | 范围查询 | 需要遍历整棵树 | 叶子节点用指针串起,直接遍历 | | 查询稳定性 | 最差 O(log n) | 稳定 O(log n),因为只有叶子节点存数据 | | IO 次数 | 更多(非叶子节点也读磁盘) | 更少(矮胖结构) | ## MySQL 中 B+ 树是怎么存的? 在 InnoDB 存储引擎中,**主键索引就是一颗 B+ 树**。 假设我们有一张用户表: ```sql CREATE TABLE `user` ( `id` bigint NOT NULL AUTO_INCREMENT, `name` varchar(50) DEFAULT NULL, `age` int DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB; ``` 当你执行 `SELECT * FROM user WHERE id = 10` 时,MySQL 干的事儿是这样的: 1. 先从**主键索引树**的根节点开始 2. 2. 根节点里存了页目录,根据 `id=10` 找到对应的指针 3. 3. 往下走,最终定位到叶子节点 4. 4. 叶子节点里存的才是完整的行数据 整个过程只需要 **3-4 次磁盘 IO**(取决于树的高度),非常快。 ### 等等,主键索引叶子节点存啥? InnoDB 的主键索引,叶子节点存的是**完整的行数据**。这就是所谓的**聚簇索引**(Clustered Index)。 那如果我建的是普通索引(非主键索引)呢? ```sql CREATE INDEX idx_age ON user(age);

普通索引的叶子节点,存的不是完整数据,而是主键值 + 索引列的值。查的时候先通过普通索引找到主键,再去主键索引树查一遍——这叫回表

一个页能存多少数据?

InnoDB 默认页大小是16KB。假设主键是 bigint(8字节),加上一些指针开销,一行数据大概占 1KB,那么一个页能存16条左右的数据。

一个 B+ 树,根节点能存 1000 多条索引,每个索引往下能分出 1000 多个子节点……算下来:

  • 高度为 2 的 B+ 树:1000 × 16 =1.6万条数据
    • 高度为 3 的 B+ 树:1000 × 1000 × 16 =1600万条数据
    • 高度为 4 的 B+ 树:能存16亿条数据
      所以绝大多数情况下,B+ 树的高度都是2-3 层,查询只需要 2-4 次磁盘 IO。

什么时候索引会失效?

既然索引这么好使,为啥有时候加了索引还是慢?常见原因:

1. 索引列用了函数或运算

-- 索引失效SELECT*FROMuserWHEREYEAR(created_at)=2024;-- 应该改成范围查询SELECT*FROMuserWHEREcreated_at>='2024-01-01'ANDcreated_at<'2025-01-01';

2. 字符串不加引号

-- 索引失效(隐式类型转换)SELECT*FROMuserWHEREphone=13800138000;-- 正确写法SELECT*FROMuserWHEREphone='13800138000';

3. 最左前缀原则

-- 建立了索引 (name, age, city)-- 可以用索引的情况WHEREname='Tom'WHEREname='Tom'ANDage=25-- 不能用索引的情况WHEREage=25WHEREcity='Beijing'

4. 模糊查询%开头的最左前缀原则

-- 索引失效SELECT*FROMuserWHEREnameLIKE'%om';-- 可以用索引SELECT*FROMuserWHEREnameLIKE'Tom%';

实战:看看你的索引长啥样

EXPLAIN看看查询有没有走索引:

EXPLAINSELECT*FROMuserWHEREid=10;

输出里的type列很重要:

type 值含义
const常量查询,用了主键索引或唯一索引
ref用了普通索引,查找多条
range范围查询
index全索引扫描
ALL全表扫描(最糟糕)

总结一下

  1. B+ 树是 MySQL 索引的底层结构,只有叶子节点存数据,用指针串起来,适合范围查询
    1. 主键索引是聚簇索引,叶子节点存完整行数据
    1. 普通索引是非聚簇索引,叶子节点存主键值,需要回表
    1. 注意索引失效的几种情况,尤其是函数、隐式转换、最左前缀
    1. 用 EXPLAIN 分析查询,确保走了索引而不是全表扫描
      好了,B+ 树原理就聊到这儿。如果还想看更多 MySQL 硬核原理,点个关注,咱们下期见。

往期热门:

  • [MySQL 事务隔离级别详解]
    • [MySQL 执行计划 EXPLAIN 详解]
    • [MySQL 慢查询优化实战]
http://www.jsqmd.com/news/833328/

相关文章:

  • 2026年4月评价高的投影机供应商实力,山体投影机/7000流明投影机/W40投影机出租,投影机销售厂家实力 - 品牌推荐师
  • 基于ChromaDB与FastAPI的Overture向量搜索引擎:开箱即用的RAG与智能体数据检索方案
  • 百度网盘提取码3秒破解:智能查询工具的终极效率革命
  • Arm MMU_S3内存管理单元架构与优化实践
  • WorkBuddy案例——教育辅导智能体
  • CircuitPython Web Workflow实战:无线开发Yoto Mini与I2C硬件验证
  • 决策拓扑:用开源工具可视化复杂业务逻辑,告别流程图乱麻
  • 六轴串联机械臂路径规划【附程序】
  • 用ESP32+GRBL打造无线写字机器人:蓝牙/WIFI控制与离线绘图全攻略
  • 5G QoS实战:从数据包到QoS Flow的映射与过滤规则全解析
  • dotai:将AI大模型无缝集成到Shell终端的智能助手工具
  • 3步极速获取百度网盘提取码:开源神器baidupankey的智能解密指南
  • MoviePilot终极指南:一键批量重命名,让你的媒体库整齐划一
  • 如何高效解析百度网盘真实下载地址:专业开发者的完整指南
  • 从零构建SDK:以Bags-SDK黑客松为例的设计、实现与实战
  • Windows右键菜单管理神器:ContextMenuManager高效清理与自定义指南
  • DHCP 中继实验:跨网段自动分配 IP(附排错实录)
  • Rust命令行工具oli:提升终端效率的轻量级瑞士军刀
  • 生产环境紧急修复如何从 tag 创建 hotfix 分支流程?
  • 表现主义不是乱涂!——掌握这8个专业级语义锚点词,让MJ瞬间理解“蒙克式焦虑”或“康定斯基式节奏”
  • 番茄小说下载器:打造属于你的个人数字图书馆终极指南
  • Python创意编程入门:用DrawBot实现矢量图形与数据可视化
  • MySQL 视图使用场景与限制
  • Scarab架构深度解析:基于Avalonia的空洞骑士模组管理器实现原理
  • 终极免费工具:NVIDIA Profile Inspector让你的显卡性能翻倍
  • 线程化笔记工具:重塑深度思考与知识管理的技术实践
  • 从零构建类Claude智能助手:基于开源LLM的指令微调与部署实战
  • 基于RAG的电影智能体构建:从向量检索到Agentic设计
  • 基于MCP协议与Figma API构建AI设计协作工具:原理与实践
  • 氛围驱动开发:用兴趣流与个性化工具链提升编程心流