MySQL索引优化从B+Tree原理到慢查询分析
一、B+Tree为什么适合数据库索引
MySQL InnoDB 默认使用 B+Tree 索引。它的特点是多路平衡、层高低、叶子节点有序并通过链表相连。相比二叉树,B+Tree 一次磁盘页能放更多 key,查找通常只需要少量随机 IO;相比哈希索引,它天然支持范围查询、排序和最左前缀匹配。
| 索引能力 | B+Tree表现 | 典型场景 |
|---|---|---|
| 等值查询 | 很好 | id、订单号 |
| 范围查询 | 很好 | 时间区间 |
| 排序 | 可利用有序性 | ORDER BY |
| 模糊前缀 | 可部分利用 | name LIKE ‘ab%’ |
| 后缀模糊 | 难利用 | LIKE ‘%ab’ |
二、联合索引与最左前缀
联合索引不是多个单列索引的简单叠加。(tenant_id, status, created_at) 的顺序决定了查询能走到哪里。等值条件通常放前面,范围字段放后面,排序字段要结合查询模式设计。
CREATE INDEX idx_order_query
ON orders (tenant_id, status, created_at);
EXPLAIN SELECT id, amount
FROM orders
WHERE tenant_id = 1001
AND status = 'PAID'
AND created_at >= '2026-07-01'
ORDER BY created_at DESC
LIMIT 20;
如果查询没有 tenant_id,这个索引很难高效使用。很多慢查询来自“看起来有索引”,但实际条件不符合最左前缀。设计索引前应先收集高频 SQL,而不是按字段名机械建索引。
三、用EXPLAIN识别问题
EXPLAIN 是慢查询分析的入口。重点看 type、key、rows、Extra。type 从好到差大致是 const、ref、range、index、ALL。如果 rows 很大且 Extra 出现 Using filesort、Using temporary,就要检查过滤条件和排序是否能被同一个索引支持。
EXPLAIN FORMAT=TREE
SELECT *
FROM orders
WHERE DATE(created_at) = '2026-07-08';
上面这种写法会对列使用函数,可能导致索引失效。应改成范围条件:
WHERE created_at >= '2026-07-08 00:00:00'
AND created_at < '2026-07-09 00:00:00'
同理,隐式类型转换、前置百分号模糊查询、低选择性字段单独建索引,都可能让优化器放弃索引。
四、慢查询治理要闭环
打开慢查询日志后,不要只盯单条 SQL 的耗时,还要看频率和扫描行数。一个 200 毫秒但每分钟执行上万次的查询,影响可能比偶发 3 秒查询更大。
SET GLOBAL slow_query_log = ON;
SET GLOBAL long_query_time = 0.5;
生产环境应通过平台化方式采集慢日志,按指纹聚合,再结合业务上下文处理。索引不是越多越好,写入、更新和磁盘占用都会随索引增加而上升。一次可靠的优化应包含:确认慢 SQL、分析执行计划、设计或调整索引、压测验证、上线观察,并清理不再使用的冗余索引。
还要关注数据分布变化。一个索引在数据量小时表现很好,半年后可能因为热点租户、状态字段倾斜或历史数据堆积而退化。可以定期查看表统计信息、索引基数和慢查询趋势。对于归档类数据,冷热分离或分区表有时比继续堆索引更有效。索引优化不是一次性动作,而是随业务增长持续校准访问路径。
上线新索引也要评估写入成本。大表加索引可能耗时较长,应选择低峰期,必要时使用在线变更工具,并提前准备回退方案。
📌 本文是《数据库性能实战》系列,持续更新,关注不迷路。
👉 下一篇:《MySQL事务隔离级别与MVCC机制解析》,讲索引之外事务可见性与锁范围如何影响并发。
💬 你在实际项目里遇到过索引存在却没被优化器选中的问题吗?评论区聊聊。
(觉得有用点个赞+收藏,方便回头查阅)
🔧 相关可运行源码/资料已整理成资源包,可在我主页的资源里自取。