mysql索引优化重在实战:从慢查询定位回表、低区分度问题,通过覆盖索引、调整字段顺序(等值前置、高区分度优先)、业务协同三步优化,并用“三星索引”验证命中、排序、覆盖。

MySQL索引优化在面试中不是考背概念,而是看能不能从一条慢查询出发,快速定位问题、解释原理、给出可落地的改进方案。下面用一个真实高频场景带出核心要点。
一、典型慢查询:订单按用户+状态查,却越来越慢
某电商系统上线半年后,以下语句响应时间从50ms升到2s以上:
SELECT * FROM orders WHERE user_id = 12345 AND status = 'paid';
表结构已有 INDEX idx_user_status(user_id, status),但EXPLAIN显示 type=ref、rows=12800,说明走了索引但扫描行数偏高。
问题不在“有没有索引”,而在“索引是否覆盖查询需求”:
- 当前是二级索引,叶子节点只存
(user_id, status, 主键order_id),而SELECT *需要全部字段 → 必须回表12800次 - status 枚举值少(pending/paid/shipped/completed),区分度低,联合索引中它排在第二位,对范围筛选帮助有限
二、三步优化:从失效诊断到覆盖设计
第一步:确认是否回表
改写为 SELECT order_id, user_id, status FROM orders WHERE user_id = 12345 AND status = 'paid';
EXPLAIN 中出现 Using index → 说明这次查询已走覆盖索引,响应降到80ms以内。
第二步:提升区分度与过滤效率
把高频、高区分度字段前移。比如增加创建时间条件:
SELECT * FROM orders WHERE user_id = 12345 AND status = 'paid' AND created_at > '2026-02-01';
对应建联合索引:ALTER TABLE orders ADD INDEX idx_user_status_time (user_id, status, created_at);
此时 created_at 参与索引过滤,大幅缩小扫描范围。
第三步:业务协同降压
对“已支付订单”这类结果集稳定、查询频繁的场景,可加缓存层;或归档历史订单(如3个月前)减少单表数据量。
三、容易踩的坑:你以为在用索引,其实没生效
面试官常追问:“如果改成 WHERE status = 'paid' AND user_id = 12345,还能用上 idx_user_status 吗?”
答案是:能。MySQL优化器会自动调整WHERE子句顺序以匹配最左前缀 —— 但这仅限于等值条件。一旦出现范围查询,顺序就关键了:
-
WHERE user_id = 12345 AND status > 'pending'→ 只用到user_id,status后面的索引列失效 -
WHERE user_id > 10000 AND status = 'paid'→user_id是范围,status完全无法利用索引
所以建联合索引时,要把等值条件放前、范围条件放后,高区分度字段优先。
四、终极建议:用“三星索引”检查你的设计
每建一个索引,默念三颗星:
-
第一星:WHERE 条件能命中索引最左前缀(如
user_id = ?) -
第二星:ORDER BY 或 GROUP BY 字段顺序与索引一致(如
ORDER BY user_id, created_at匹配(user_id, created_at)) - 第三星:SELECT 的所有字段都在索引中(覆盖索引),避免回表
三颗星齐备,才是高性能索引。不追求多,而追求每一颗星都亮。









