联合索引的前缀规则
复合索引 (a, b, c) 只有在从最左列开始连续使用时才有效:只写 b = ? 用不上这棵索引,写了 a = ? AND c = ? 也只能用上 a 这一列,因为 c 必须等 b 也被使用后才能继续参与定位。某一列一旦使用范围条件,后续列就无法继续用于索引查找。设计索引时把等值条件列放前面、范围列放后面,并尽量让列顺序覆盖更多查询。用 EXPLAIN 的 key_len 可以推断实际用到了前几列。
基本信息
| 规则 | 从最左列开始连续使用才有效 |
|---|---|
| 跳过前导列 | 只写中间或后面的列,索引全部失效 |
| 范围截断 | 某列用范围条件后,后续列无法继续用于定位 |
| 设计原则 | 等值列在前,范围列在后 |
| 验证手段 | EXPLAIN 的 key_len 推断命中的列数 |
| 适用平台 | MySQL 与 PostgreSQL 的 B 树索引规则一致 |
典型使用场景
- 多条件查询建索引:按条件的等值与否决定列顺序
- 判断现有索引能否复用:核对查询条件是否从最左列开始
- 优化列表查询:常见的状态加时间组合
- 调试慢查询:用 key_len 确认只用到了前几列
- 评估新增索引的必要性:能不能被现有索引覆盖
- 规范表设计:索引列顺序写进开发约定
示例
示例
ALTER TABLE orders ADD INDEX idx_abc (user_id, status, created_at);
-- 能有效使用索引
SELECT * FROM orders WHERE user_id = 100;
SELECT * FROM orders WHERE user_id = 100 AND status = 'paid';
SELECT * FROM orders WHERE user_id = 100 AND status = 'paid' AND created_at > '2024-01-01';
-- 用不上(未从最左列开始)
SELECT * FROM orders WHERE status = 'paid';
SELECT * FROM orders WHERE created_at > '2024-01-01';
-- 中途列用范围条件:created_at 条件仍可过滤,但不再进入索引定位
SELECT * FROM orders WHERE user_id = 100 AND status > 'p' AND created_at > '2024-01-01';使用情况对照
| 查询条件 | 索引用到哪几列 | 说明 |
|---|---|---|
| a = ? | 第 1 列 | 有效 |
| a = ? AND b = ? | 前 2 列 | 有效 |
| a = ? AND b = ? AND c = ? | 全部 3 列 | 有效 |
| b = ? 或 c = ? | 未使用 | 未从最左列开始 |
| a = ? AND c = ? | 只有第 1 列 | c 被 b 隔断 |
| a = ? AND b > ? AND c = ? | 前 2 列 | b 是范围,c 不再参与定位 |
key_len 是判断命中了前几列的实用手段:把 key_len 与各列的定义长度对照,就能估算实际用到了哪几列。
MySQL 有索引下推(ICP),范围截断后的列条件仍可能在存储引擎层过滤,减少回表次数,但它不等于索引定位,扫描量依旧可观。
查询条件的书写顺序不影响索引使用,真正决定的是索引本身的列顺序,这一点常被误解。
设计要点
- 先收集高频查询,再按等值条件在前、范围条件在后排列
- 把区分度高的列放前面,能让索引更快缩小范围
- 覆盖高频查询的多个条件,避免为每种组合各建一棵索引
- 定期清理冗余索引:最左前缀相同的索引大多可以合并
- 新增索引前先用 EXPLAIN 验证查询确实会用到它
- 索引越多写入越慢,权衡读性能与写成本