复合索引能加速JOIN,因其将JOIN字段与WHERE条件字段按最左前缀组织为B+树,使数据库可直接定位匹配行而非全表扫描;例如ON a.user_id = b.id AND b.status = 'paid'时,索引(b.id, b.status)可高效查找,但若索引为(b.status, b.id)则JOIN失效。

复合索引为什么能加速 JOIN?
因为 JOIN 的本质是按字段值匹配行,而复合索引把多个字段的值按顺序组织成一棵 B+ 树——只要 ON 条件中用到的字段组合,能被索引的最左前缀覆盖,数据库就能跳过全表扫描,直接定位匹配行。比如 ON a.user_id = b.id AND b.status = 'paid',如果 b 表有索引 (id, status),那么每次从 a 表取一条 user_id 时,b 表就能用该索引快速查出对应 id 且 status = 'paid' 的记录,不用扫全表。
但注意:这个索引对 WHERE b.status = 'paid' 单独查询无效,因为没用上最左列 id;反过来,如果索引是 (status, id),那对 ON a.user_id = b.id 也几乎无效,因为 id 不在最左位置,无法用于等值查找。
JOIN 字段和 WHERE 条件怎么一起进复合索引?
关键不是“哪个先写”,而是“哪个字段过滤性更强、哪个字段参与 JOIN”。优先把高选择性的等值条件字段放最左,再放范围条件(如 >、BETWEEN),最后放 JOIN 字段(如果它本身不是高选择性等值字段)。
- 例如查询:
SELECT * FROM orders o JOIN users u ON o.user_id = u.id WHERE u.status = 1 AND u.ctime > '2024-01-01'→u表建议建索引:(status, ctime, id)(status等值 +ctime范围 +id用于 JOIN) - 如果改成
WHERE u.ctime > '2024-01-01' AND u.status = 1,索引仍生效,因为优化器会自动重排条件顺序去匹配最左前缀 - 但若
WHERE中只有u.ctime > '2024-01-01',而索引是(status, ctime, id),那就只能走ctime的范围扫描,效率远不如(ctime, status, id)
哪些情况会让复合索引在 JOIN 中完全失效?
最常见的是在 JOIN 字段上用了函数或隐式类型转换。哪怕只加一个 DATE() 或把 INT 和 BIGINT 对连,索引就掉线了。
-
ON DATE(t1.create_time) = DATE(t2.date)→ 两个字段都丢了索引,变成逐行计算比对 -
ON t1.user_id = t2.uid,但t1.user_id是INT、t2.uid是BIGINT→ 类型不一致,MySQL 可能放弃使用任一索引 -
ON t1.name = UPPER(t2.name)→ 函数作用于索引列,无法走索引 - 外键字段没手动建索引 → 外键约束 ≠ 自动索引,
ALTER TABLE加完约束后必须单独CREATE INDEX
EXPLAIN 里怎么看复合索引有没有真起作用?
不能只看 key 列显示了索引名,重点看三处:
-
type是ref或eq_ref,而不是ALL或index—— 说明走了索引查找,不是扫描 -
rows值明显小于该表总行数 —— 表示实际扫描行数少,过滤有效 -
Extra里没有Using where; Using join buffer这类提示 —— 后者常意味着驱动表结果集太大,被迫用块嵌套循环(BNL),性能已退化
如果发现大表当了驱动表但 type = ALL,别急着调顺序,先确认它有没有针对 JOIN 和 WHERE 字段建对复合索引——很多时候问题不在写法,而在索引缺位。










