联合索引字段顺序应按「区分度高 + 筛选性强 + 稳定性好」权衡,优先将高区分度字段(如 user_id)置最左,避免常量或倾斜字段(如 status=1 占 95%)放最左,防止索引失效和回表放大。

联合索引字段顺序怎么定才不踩坑
字段顺序直接决定索引能否命中,不是按查询频率排,而是按「区分度高 + 筛选性强 + 稳定性好」三者权衡。比如 status 只有 0/1,created_at 每秒都在变,user_id 有百万级唯一值——这时候把 user_id 放最左,比放最后有效十倍。
常见错误是把常量条件字段(如 is_deleted = 0)放在索引最左,结果全表扫描照旧。MySQL 只能高效使用索引的「最左前缀」,一旦遇到范围查询(>、BETWEEN、LIKE 'abc%')或等值失效(IS NULL 后跟其他条件),右边字段就失效了。
-
WHERE user_id = 123 AND status = 1 AND created_at > '2024-01-01'→ 推荐索引:(user_id, status, created_at) -
WHERE status = 1 AND created_at > '2024-01-01' AND user_id = 123→ 同样走(user_id, status, created_at),但 MySQL 会自动调整顺序匹配最左前缀 - 如果
status倾斜严重(95% 是 1),它就不该当最左字段;换成(user_id, created_at, status)更稳
数据倾斜时为什么 EXPLAIN 显示走了索引却很慢
因为 MySQL 优化器看到「走了索引」就以为万事大吉,其实底层在回表时遭遇了大量重复值——比如 status = 1 匹配 50 万行,哪怕 user_id 在索引里,也要逐条回主键查完整记录,I/O 爆涨。
典型现象:type 是 ref 或 range,但 rows 高得离谱,Extra 出现 Using where; Using index 却响应超时。
- 用
SELECT COUNT(*) FROM t WHERE status = 1和COUNT(*) WHERE status = 0对比,确认倾斜程度 - 对严重倾斜字段,考虑拆分查询:先用覆盖索引查出 ID 列表(加
LIMIT控制数量),再用IN分批查详情 - 避免在倾斜字段上建单列索引,它几乎没用;联合索引里它只能当「后缀」,且要配合高区分度字段前置
ORDER BY 和联合索引排序失效的几种情况
联合索引能避免文件排序(Using filesort),前提是 ORDER BY 字段顺序和索引定义完全一致,且所有排序方向相同(全 ASC 或全 DESC)。MySQL 8.0+ 支持混合方向,但 5.7 及以前一律不认。
更隐蔽的问题是:WHERE 条件用了范围,后面字段即使出现在 ORDER BY 里,也无法利用索引排序。
-
INDEX (a, b, c),查询WHERE a = 1 AND b > 10 ORDER BY c→ ✅ 有效 -
WHERE a > 1 ORDER BY b, c→ ❌b和c排序失效,因a是范围,索引从b开始已无序 -
WHERE a = 1 ORDER BY b ASC, c DESC(MySQL 5.7)→ ❌ 强制Using filesort
什么时候该放弃联合索引,改用冗余索引或物化路径
当业务查询模式高度发散,比如同一张表既要按 (user_id, status) 查,又要按 (status, created_at) 查,还要按 (created_at, type) 查,硬凑一个联合索引只会让每个查询都慢半拍。
这时冗余索引不是浪费,而是止损。但要注意:冗余索引会拖慢写入,且 ALTER TABLE 加索引期间锁表(除非用 ALGORITHM=INPLACE)。
- 优先保留高频、低延迟查询的索引,砍掉半年没被
EXPLAIN过的 - 用
SELECT * FROM performance_schema.table_io_waits_summary_by_index_usage查真实使用率(需开启 performance_schema) - 对树形结构或层级关系强的数据,比方说分类、菜单,与其堆联合索引,不如存
path字段(如/1/5/23/),用前缀索引加速
最麻烦的不是建错索引,是建了之后没人看慢查日志、没人核对 Handler_read_* 指标。倾斜不会自己说话,它只在凌晨三点用超时告警敲你门。