MySQL 索引优化入门:从慢查询到覆盖索引
数据库用久了,最常见的抱怨就是“查询越来越慢”。表里只有几千行时一切正常,数据涨到几十万行后,原本毫秒级返回的接口开始频繁超时。很多人的第一反应是升级硬件,其实大多数慢查询问题都可以通过合理使用索引解决。
一、索引到底是什么
可以把索引理解为一本书的目录。没有目录时,要找某个章节只能从第一页翻到最后一页,这就是全表扫描;有了目录,就可以直接定位到目标页码,代价是目录本身也要占用篇幅,并且每次增删内容都要同步更新目录。
MySQL 的 InnoDB 引擎默认使用 B+ 树来组织索引。B+ 树是一种多路平衡查找树,叶子节点按顺序排列并用链表连接,因此既适合等值查询,也适合范围查询。主键索引的叶子节点直接存放整行数据,这叫聚簇索引;普通索引的叶子节点存放的是主键值,查到主键后再回表取整行数据,这叫二级索引。
二、先找到慢查询:EXPLAIN
优化之前要先定位问题。MySQL 提供了 EXPLAIN 语句,可以查看一条查询的执行计划:
EXPLAIN SELECT * FROM orders WHERE user_id = 1001 AND status = 'paid';
重点关注几个字段:type 表示访问类型,从好到差依次是 system、const、eq_ref、ref、range、index、ALL,出现 ALL 就意味着全表扫描;key 表示实际用到的索引,为 NULL 说明没有用上索引;rows 是预估扫描行数,这个数字越小越好;Extra 里的 Using filesort 和 Using temporary 则提示需要额外的排序或临时表,值得警惕。
三、最左前缀原则
联合索引是索引优化中最容易踩坑的地方。假设在 (user_id, status, created_at) 三列上建了联合索引,那么查询条件里必须从最左边的列开始匹配:只用 user_id 可以走索引,只用 status 则走不了;user_id 和 created_at 组合时,只能用到 user_id 这一列的部分索引。
一个实用的建索引顺序是:区分度高的列放前面,等值查询的列放前面,范围查询的列放后面。因为范围条件会截断后续列的索引使用,把范围列放在最后可以让前面的列充分发挥作用。
四、覆盖索引与回表
如果查询需要的列全部包含在索引里,MySQL 就不需要回表取数据,这种索引叫覆盖索引。比如下面的查询:
SELECT user_id, status FROM orders WHERE user_id = 1001;
如果存在 (user_id, status) 索引,这条查询可以直接从索引中拿到结果,Extra 会显示 Using index,速度通常比回表快一个数量级。设计索引时可以有意识地把高频查询的列组合进去,但也要注意索引列越多,写入成本和存储占用就越高。
五、索引不是越多越好
索引能加速查询,却会拖慢写入。每次 INSERT、UPDATE、DELETE 都要维护相关的索引树,索引越多,写入越慢,占用磁盘也越多。常见的反模式包括:给区分度极低的列建索引、在小表上建一堆索引、以及建了大量从未被使用过的索引。可以用慢查询日志定期检查索引的使用情况,把长期无人使用的索引删掉,保持索引精简,是数据库长期健康的习惯。
六、一个完整的优化例子
假设订单表 orders 有 200 万行,查询“某个用户最近三个月已支付的订单”原来要 2 秒多:
SELECT id, amount, created_at FROM orders
WHERE user_id = 1001 AND status = 'paid'
AND created_at >= '2026-07-27'
ORDER BY created_at DESC;
优化思路是建一个 (user_id, status, created_at) 联合索引:前两列是等值条件,最后一列既是范围条件又是排序列。建好索引并用 EXPLAIN 验证后,这条查询通常可以降到几毫秒。记住:先验证,再上线,任何索引调整都要以执行计划为准。
结语
索引优化的核心并不复杂:理解 B+ 树的结构,用 EXPLAIN 定位问题,遵循最左前缀原则,善用覆盖索引,同时克制建索引的数量。把这几条原则用在日常开发里,大多数慢查询问题都能在萌芽阶段解决。
- 点赞
- 收藏
- 关注作者
评论(0)