【第5天】每天一个MySQL知识点,百日打怪升级

本文涉及的产品
PolarDB Agent Express,2核4GB
PolarSearch,搜索节点 4核8GB
PolarDB Agent Flow,2核4GB
简介: 本文是MySQL索引核心篇,由10年经验DBA精讲B+树原理。深入剖析为何选B+树而非B树/二叉树/哈希表,详解其非叶节点仅存key、叶子双向链表、聚簇与二级索引结构,并结合回表、覆盖索引、页分裂等实战场景,直击面试高频考点。(239字)

索引基础:B+树详解


大家好,我是一名拥有10年以上经验的DBA老兵。

做这个系列,源于一个朴素的愿望:把踩过的坑、总结的经验系统化输出,希望能帮到刚入行或想进阶的兄弟们。

让我们开始今天的第5天内容。


背景引入

💡 说白了:索引就是一本书的目录。没有目录,你想找"事务"这个词,只能一页一页翻;有了目录,直接翻到对应页码。

很多同学知道索引能加速查询,但面试官一问"B+树为什么比B树适合做索引?""为什么不用二叉树?"——就懵了。

今天的目标:搞懂B+树结构,面试必问!


核心概念

一、没有索引会怎样?

没有索引时,MySQL只能全表扫描——从第一行开始,逐行对比,直到找到目标。

-- 没有索引时,查找name='张三'要扫描全表
SELECT * FROM user WHERE name = '张三';

-- EXPLAIN 查看执行结果(type=ALL,说明全表扫描)
-- type列值:ALL → 全表扫描,性能最差

假设一张表100万行,找一个值最多要对比100万次。这就是为什么索引这么重要。


二、为什么是B+树?

面试官喜欢问:为什么MySQL索引用B+树,而不是二叉树、哈希表?

我们逐个对比:

数据结构 查找复杂度 适合场景 不适合做索引的原因
二叉搜索树 O(log n) 内存查找 树太高,磁盘IO多
哈希表 O(1) 等值查找 不支持范围查询
B树 O(log n) 早期数据库 非叶子节点存数据,一次IO拿到的key少
B+树 O(log n) MySQL InnoDB ✅ 叶子节点存数据,非叶子节点只存key

💡 说白了:B+树就像一栋楼——电梯口(非叶子节点)只放楼层号,每间房(叶子节点)放真正的数据。电梯口越小,一层能放的楼层号越多,树就越矮,IO就越少。


三、B+树的关键特征

3.1 非叶子节点只存key,不存数据

好处:非叶子节点只存key,一个页(16KB)能存更多key,树更矮,IO更少。

3.2 叶子节点用双向链表连接

这是B+树和B树最大的区别——B+树的叶子之间有指针,B树没有

好处

  • 范围查询极快:找到起点后,顺着链表往后扫就行
  • 排序查询天然有序:ORDER BY不用额外排序

💡 说白了:B+树的叶子像一排人手拉手,找到第一个人,后面的跟着就出来了。B树呢?每找一个人都要重新回上一层找,效率差远了。

面试必问

  • B+树和B树有什么区别?
  • 为什么MySQL用B+树而不是B树做索引?

📝 面试解答

Q: B+树和B树有什么区别?

image.png

image.png

三个核心区别:

  • 非叶子节点:B+树只存key,B树存key+数据 → B+树一个页能存更多key,树更矮
  • 叶子节点:B+树所有数据都在叶子,B树数据分散在各层 → B+树查询稳定,都是O(log n)
  • 叶子链表:B+树叶子之间有双向链表,B树没有 → B+树范围查询极快

Q: 为什么MySQL用B+树而不是B树做索引?

  1. IO更少:B+树非叶子只存key,同样16KB的页能存更多key,树更矮,IO次数更少
  2. 范围查询更快:叶子之间有链表,找到起点后顺着扫就行;B树要中序遍历
  3. 查询更稳定:所有数据都在叶子节点,每次查询路径长度一样

四、InnoDB的索引结构

InnoDB有两种索引:聚簇索引二级索引

4.1 聚簇索引(主键索引)

聚簇索引的叶子节点存的是整行数据

image.png

💡 聚簇索引 = 数据本身。InnoDB的数据就是按主键顺序存的,所以主键查询是最快的

4.2 二级索引(非主键索引)

二级索引的叶子节点存的是主键值,不是整行数据。

image.png

查name='张三',先用二级索引找到主键id=30,再回聚簇索引找整行数据——这个过程叫回表

面试必问

  • 什么是聚簇索引?什么是二级索引?
  • 什么是回表?

📝 面试解答

Q: 什么是聚簇索引?什么是二级索引?

  • 聚簇索引:叶子节点存整行数据,数据按主键顺序存储。一张表只有一个聚簇索引。
  • 二级索引:叶子节点存主键值,查到主键后需要回聚簇索引取完整数据。

Q: 什么是回表?

通过二级索引找到主键值,再回聚簇索引查整行数据的过程叫回表

回表意味着多一次B+树查找,所以二级索引比主键索引慢。

避免回表的方法:覆盖索引(后续章节讲)——让查询只需要走二级索引就能拿到所有需要的列。


实战案例

场景一:查看索引结构

-- 创建测试表及执行结果
CREATE TABLE t_index_demo (
    id INT PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(50),
    age INT,
    INDEX idx_name (name)
) ENGINE=InnoDB;

-- 查看索引
SHOW INDEX FROM t_index_demo;
-- 结果:会看到两个索引
-- 1. PRIMARY(聚簇索引,列id)
-- 2. idx_name(二级索引,列name)

-- 用EXPLAIN观察索引使用
EXPLAIN SELECT * FROM t_index_demo WHERE id = 1;
-- type=const,走主键索引,最快

EXPLAIN SELECT * FROM t_index_demo WHERE name = '张三';
-- type=ref,走二级索引,需要回表

场景二:观察回表过程

-- 回表示例及执行结果
-- 只查主键,不需要回表
EXPLAIN SELECT id FROM t_index_demo WHERE name = '张三';
-- Extra: Using index → 覆盖索引,不回表!

-- 查所有列,需要回表
EXPLAIN SELECT * FROM t_index_demo WHERE name = '张三';
-- Extra: NULL 或 Using where 或留空 → 回表查询

💡 说白了:查 id 不回表(二级索引叶子就有),查 * 要回表(二级索引叶子没age等列)。


场景三:B+树的分裂

-- 顺序插入(主键自增,不分裂)
-- InnoDB主键自增时,B+树顺序追加,不会发生页分裂
INSERT INTO t_index_demo (name, age) VALUES ('张三', 25);
INSERT INTO t_index_demo (name, age) VALUES ('李四', 30);
-- ... 顺序插入,性能好

-- 乱序插入(可能触发页分裂)
-- 如果主键不是自增的,插入可能触发B+树的页分裂,导致性能下降

💡 这也是为什么主键推荐自增——顺序插入不会触发页分裂,性能最好。


避坑指南

⚠️ 真实踩过的坑:

  1. 主键乱序导致页分裂

    • 主键不是自增的(如UUID),插入时B+树频繁分裂
    • 建议用自增主键,顺序插入效率最高
  2. 二级索引回表导致慢查询

    • SELECT * 走二级索引必须回表,比主键索引慢
    • 能用覆盖索引就用覆盖索引,避免回表
  3. 索引列数据太大影响B+树高度

    • 索引列越短,一页存越多key,树越矮
    • VARCHAR(255)做索引比INT做索引,树更高更多IO
  4. 混淆聚簇索引和二级索引

    • 聚簇索引只有一张表一个,就是主键
    • 二级索引叶子存的是主键值,不是数据行指针

思考题

🤔 互动时间:

  1. 为什么InnoDB表必须有主键?如果没有定义主键会发生什么?
  2. 覆盖索引为什么不需要回表?

总结

🎯 面试考点

  • B+树 vs B树:非叶子只存key、叶子有链表、查询稳定
  • 聚簇索引:叶子存整行数据,按主键顺序存储
  • 二级索引:叶子存主键值,需要回表
  • 回表:二级索引→主键值→聚簇索引→整行数据
  • 自增主键避免页分裂,性能最优

💡 AI实战建议:让自己的AI助手记住建表规范检查清单,每次写CREATE TABLE时自动检查:

  • 必须有主键,且推荐自增
  • 索引命名遵循规范:普通索引以 idx_ 开头,唯一索引以 uk_ 开头
  • 联合索引列数不超过5列

下期预告:索引优化:何时建索引、何时不建 —— 面试必问!

全本合集《每天一个MySQL知识点,百日打怪升级》 您的关注是我每日更新的动力


有问题欢迎评论区交流,明天见!

相关文章
|
2月前
|
SQL 存储 关系型数据库
【第1天】每天一个MySQL知识点,百日打怪升级
本系列以“每天一个知识点”形式,系统讲解MySQL核心原理。首日聚焦Client/Server通信流程,详解三层架构(连接层→SQL层→存储引擎层)、连接管理、查询解析优化及执行计划,直击面试高频考点,助你从CRUD进阶到懂原理、能排障。(239字)
225 0
|
1月前
|
人工智能 运维 架构师
我在 AIP 智能体平台踩过的坑,都在这篇企业 AI 落地经验里了
软件架构师罗小东分享企业AI落地实战经验:聚焦AIP智能体平台建设中的真实坑点与解法——涵盖智能体全生命周期管理、多源知识库语义检索、MCP工具集成及多模型中立架构设计,强调“解决问题”而非堆砌功能。(239字)
|
2月前
|
机器学习/深度学习 搜索推荐 算法
拆解推荐系统:候选生成、过滤、排序、多样性的分层设计
推荐系统是端到端流水线,非单一算法:涵盖候选生成、过滤、特征工程、多目标排序、多样性调控与反馈闭环。强调关注点分离,以保障质量、速度与行为可控。动手前须明确定义Item、用户行为及成功指标。
425 12
拆解推荐系统:候选生成、过滤、排序、多样性的分层设计
|
2月前
|
人工智能 JavaScript API
(技术贴)别被全网爆火的OpenClaw骗了!实测2小时,真不适合普通人
别被全网爆火的OpenClaw误导!实测2小时发现:部署卡顿、API成本高(日耗几十至千元)、报错难排查,需懂命令行与调试——它本质是开发者框架,非普通人开箱即用工具。现阶段,等待成熟或选择成熟产品更明智。
461 6
|
1月前
|
安全 Linux 数据库
Omnissa Horizon 8 2603 发布 - 虚拟桌面基础架构 (VDI) 和应用软件
之前称为 VMware Horizon, 通过高效、安全的虚拟桌面交付增强您的工作空间
202 3
Omnissa Horizon 8 2603 发布 - 虚拟桌面基础架构 (VDI) 和应用软件
|
1月前
|
缓存 安全 搜索推荐
[004][缓存模块]Caffeine缓存自定义:构建灵活的Spring Boot缓存管理器
本文介绍Spring Boot中Caffeine缓存的灵活定制方案:通过自定义`FlexibleCaffeineCacheManager`,支持按缓存名(如users/products)独立配置过期策略、容量等参数,兼顾全局默认与个性化需求;结合线程安全创建器、属性合并机制及无缝Spring集成,实现高性能、易扩展、零侵入的本地缓存管理。(239字)
125 2
|
1月前
|
缓存 NoSQL Java
[006][缓存模块] 两级缓存实战:基于 Caffeine + Redis 的多级缓存设计与实现
本文介绍基于Caffeine(本地)+ Redis(分布式)的两级缓存实战方案,通过自定义`MultiLevelCache`与`MultiLevelCacheManager`,实现Spring Cache标准接口下的透明多级缓存:读优先本地(纳秒级)、未命中查Redis并回填;写同步更新两级,兼顾高性能与数据共享。代码开源可直接集成。
191 0
|
2天前
|
SQL 人工智能 关系型数据库
【MySQL百日打怪升级第24天】EXPLAIN 执行计划解读 —— type/key/rows/Extra
本文详解MySQL执行计划核心字段:type(访问类型,ALL/index需警惕)、rows(扫描行数,重在比例)、Extra(性能关键,关注Using index/filesort等)。结合实战案例与索引优化技巧,助DBA及开发者快速定位慢查根源,直击面试高频考点。
115 5
|
1月前
|
缓存 NoSQL Java
[012][缓存模块]基于 Spring Cache 的缓存操作模版,支持Caffeine缓存, Redis缓存及两级缓存
本项目基于Spring Cache抽象,提供Caffeine本地缓存、Redis分布式缓存及两级缓存(Caffeine+Redis)的统一操作模板。通过工厂模式与模板方法,封装get/put/delete/异步加载等能力,支持租户隔离、类型安全与Spring生命周期集成,显著简化缓存接入。(239字)
107 3
[012][缓存模块]基于 Spring Cache 的缓存操作模版,支持Caffeine缓存, Redis缓存及两级缓存
|
2月前
|
NoSQL 网络协议 Cloud Native
【Azure Redis】云原生环境下的 Redis 超时之谜:为什么 15 分钟后应用才恢复?
云原生中Redis短暂不可用后应用持续超时15分钟?问题不在Redis,而在Linux TCP默认重传机制(tcp_retries2=15)与长连接模型的错位。需三管齐下:调低内核重传次数、客户端显式配置超时与自动重连、应用层引入断路器与弹性重试。
226 20