订阅更新

我有三个查询条件,组合索引怎么设计?顺序怎么定?

MySQL手册 第 8 题 约 3 分钟读完

本文目录
  1. 先说结论
  2. 业务场景:订单查询的三个条件
  3. 原则一:等值条件在前,范围条件在后
  4. 原则二:高区分度字段优先
  5. 原则三:考虑ORDER BY和覆盖索引
  6. 实战经验:不要加太多索引
  7. 总结

先说结论

组合索引字段顺序的核心原则:等值条件在前,范围条件在后,高区分度优先。

先把这段讲顺;面试官追问时,再展开下面的原理和细节。

详细讲解

组合索引设计是生产环境里最常做的优化操作。但很多人不知道字段顺序怎么定,随便排一个顺序,结果索引效果差很多。今天把组合索引设计的原则和实战经验讲清楚。

业务场景:订单查询的三个条件

-- 常见的订单查询
SELECT * FROM orders
WHERE status = 'paid'            -- 等值条件
  AND user_id = 12345            -- 等值条件
  AND created_at > '2025-03-01'  -- 范围条件
ORDER BY created_at DESC
LIMIT 20;

三个条件:status(等值)、user_id(等值)、created_at(范围)。索引怎么设计?

原则一:等值条件在前,范围条件在后

因为B+树索引是按字段顺序从左到右排序的。等值条件可以精确定位,范围条件只能确定一个范围。范围条件后面的字段无法使用索引。

-- ✖ 错误顺序:(created_at, status, user_id)
-- created_at是范围条件,放在最前,后面的status和user_id无法用索引
 
-- ✔ 正确顺序:(status, user_id, created_at)
-- status和user_id是等值,精确定位后,created_at的范围查询也能用上索引
ALTER TABLE orders ADD INDEX idx_s_u_c (status, user_id, created_at);

原则二:高区分度字段优先

区分度 = 该字段的不同值数量 / 总行数。区分度越高,过滤效果越好。

user_id的区分度远高于status(status可能只有5个值)。所以如果status和user_id都是等值条件,可以把user_id放前面:

-- 更优的顺序(user_id区分度更高)
ALTER TABLE orders ADD INDEX idx_u_s_c (user_id, status, created_at);

但实际中还要看查询模式。如果有些查询只带status不带user_id,那把status放前面更通用(最左前缀原则)。

原则三:考虑ORDER BY和覆盖索引

如果查询有ORDER BY created_at DESC,把created_at放在索引末尾,MySQL可以直接用索引排序,避免Using filesort。

如果SELECT的字段都在索引里,还可以做成覆盖索引,避免回表:

-- 覆盖索引:索引包含查询需要的所有字段
ALTER TABLE orders ADD INDEX idx_cover (status, user_id, created_at, order_id, amount);
-- EXPLAIN显示Using index,不需要回表

实战经验:不要加太多索引

索引不是越多越好。每个索引都会占用磁盘空间,而且写入时要维护所有索引。我的经验是:一张表的索引不超过5个,每个组合索引不超过5个字段。如果超过了,要重新审视是不是查询模式有问题。

总结

组合索引三原则:等值在前范围在后、高区分度优先、兼顾ORDER BY和覆盖索引。面试时能用一个真实的查询场景现场设计索引,并解释为什么这么排序,就很有说服力。

下一篇聊一个大数据开发经常遇到的场景:MySQL的Binlog是什么?它和大数据有什么关系?

你们生产环境的索引是怎么设计的?评论区聊聊~

关注「Wood聊大数据」| 9年大数据老兵,聊技术、聊职场、聊成长

刷题进度保存在当前浏览器