索引字段顺序应由查询模式决定而非基数高低;等值条件须在范围条件前;高频必带的低基数字段可放首位;需结合EXPLAIN和慢查询日志持续优化。

高基数字段放前面 ≠ 索引一定高效
因为索引是否生效,不只取决于单列值的“唯一性高低”,而取决于查询实际怎么用它。比如 user_id 基数百万,status 只有 0/1 两个值,但若 95% 的查询都写 WHERE status = 'active',而几乎从不单独查 user_id,那把 status 放第一列反而更关键——否则索引压根不会被选中。
等值条件必须优先于范围条件
联合索引一旦遇到范围查询(如 >、、<code>BETWEEN),其右侧所有列就失效。所以即使 created_at 是时间戳、基数极高,只要查询里带 WHERE created_at > '2024-01-01',它就不该放前面。
- 错误顺序:
(created_at, user_id)→ 查询WHERE created_at > ? AND user_id = 123中user_id完全无法走索引 - 正确顺序:
(user_id, created_at)→ 先等值定位用户,再在其数据范围内做时间范围扫描 - 验证方法:用
EXPLAIN看key_len,越接近索引总字节数,说明用得越全
低基数字段不是不能放前面,而是要看它是否高频必带
像 status、is_deleted 这类字段,本身区分度差,但若业务逻辑强制所有查询都加 WHERE status = 'active',那它就是事实上的“过滤入口”。跳过它,等于放弃整个索引的最左前缀匹配能力。
- 典型场景:后台管理页默认只查有效订单,SQL 总带
AND status = 'paid' - 此时
(status, user_id, created_at)比(user_id, status, created_at)更实用 - 陷阱:如果某条查询漏了
status条件,又没其他等值条件打头,这个索引就彻底闲置
真正决定顺序的是查询模式,不是字段统计值
用 SELECT COUNT(DISTINCT col)/COUNT(*) 算选择性只是辅助参考,不能代替对真实 SQL 的分析。一个字段在表里唯一值再多,如果查询里从不拿它做等值过滤,或者总配合函数使用(如 WHERE DATE(created_at) = '2024-01-01'),那它放哪都没用。
- 必须检查慢查询日志里高频出现的 WHERE 条件组合
- 注意隐式类型转换:比如
user_id是BIGINT,但代码传参写成字符串'123',会导致索引失效 - 排序和分组字段也要纳入考虑:如果常查
WHERE user_id = ? ORDER BY created_at DESC,(user_id, created_at)就比单纯优化 WHERE 更全面
EXPLAIN 对照线上真实 SQL 重验。


















