复合索引必须从最左列开始匹配,否则无法利用B+树有序结构快速定位;WHERE条件顺序不影响优化器选择,但缺失最左列或对其使用函数将导致索引失效。

因为复合索引底层是 B+ 树,数据按定义顺序逐级排序,跳过最左列就找不到查找起点——这不是 MySQL 的限制,而是存储结构决定的必然结果。
复合索引的 B+ 树结构决定了必须从最左列开始匹配
比如索引 idx_user_status_ctime(user_id, status, create_time),B+ 树实际存储逻辑是:先全局按 user_id 升序;user_id 相同时,再按 status 升序;前两者都相同时,再按 create_time 升序。
这意味着:
-
status只在每个user_id分支内有序,跨user_id值就完全无序 -
create_time更是只在user_id + status完全匹配的子块里有序 - 查
WHERE status = 'paid'时,MySQL 不知道该去哪个user_id分支找,只能全表扫描
WHERE 条件顺序不影响索引匹配,但缺失最左列就彻底失效
MySQL 查询优化器会自动重排 WHERE 子句顺序,所以这三条语句效果等价:
WHERE user_id = 100 AND status = 'paid'WHERE status = 'paid' AND user_id = 100WHERE user_id = 100 AND status = 'paid' AND create_time = '2025-01-01'
但只要 user_id 没出现在 WHERE 中(哪怕只在 ORDER BY 或 GROUP BY 里),这个复合索引大概率不会被选中。
范围查询和函数操作会直接截断或废掉整个索引路径
一旦某列用了范围操作(>、<、BETWEEN、LIKE 'abc%'),右边所有列就不再参与索引查找,仅用于回表后过滤:
-
WHERE user_id = 100 AND status > 'canceled' AND create_time = '2025-01-01'→ 索引只用到status,create_time不走索引查找 -
WHERE user_id = 100 AND status = 'paid' AND create_time > '2025-01-01'→user_id和status走索引定位,create_time是范围终点,之后无列可用 -
WHERE YEAR(create_time) = 2025→ 整个复合索引失效,B+ 树没法对函数变换后的值建立有序路径
真正容易被忽略的是:即使你写了全部三列,只要最左列没出现在 WHERE 条件中,或者被函数包裹(如 UPPER(name)、DATE(created_at)),索引就无法按 B+ 树路径快速定位——这不是执行计划没选对,是物理上根本走不通。


















