一、核心概念

最左前缀匹配是MySQL联合索引(复合索引)的一个重要规则:在使用联合索引时,查询条件必须从索引的最左列开始,并且不能跳过中间列。

例如,对于联合索引 INDEX(a, b, c)

  • ✅ 可以使用:WHERE a=1WHERE a=1 AND b=2WHERE a=1 AND b=2 AND c=3
  • ❌ 无法使用:WHERE b=2WHERE c=3WHERE b=2 AND c=3

二、原理详解

1. B+树存储结构

联合索引在B+树中的存储方式是先按第一列排序,第一列相同时按第二列排序,以此类推

INDEX(a, b, c) 为例,数据在B+树中的排列:

(1, 1, 1)
(1, 1, 2)
(1, 2, 1)
(1, 2, 2)
(2, 1, 1)
(2, 1, 2)
(2, 2, 1)

2. 为什么必须遵守最左前缀

本质原因:B+树的有序性只对最左列有效,后续列只在前面列相同的情况下才是有序的。

  • 场景1:查询 WHERE a=1 → a列全局有序,可以快速定位
  • 场景2:查询 WHERE b=2 → b列在全局范围内是无序的(如上例,b=1和b=2交替出现),无法使用二分查找
  • 场景3:查询 WHERE a=1 AND c=3 → 可以利用a定位范围,但c在该范围内无序,需要回表检查

3. 最左前缀的范围

“最左前缀”不仅指完整的列,还包括:

  • 字符串前缀INDEX(name) 可以匹配 WHERE name LIKE '张%'
  • 连续列组合INDEX(a,b,c) 中的 (a)(a,b)(a,b,c) 都是有效前缀

三、典型场景与注意事项

1. 范围查询的影响

-- 索引 INDEX(a, b, c)
WHERE a=1 AND b>10 AND c=5
  • a:精确匹配,使用索引
  • b:范围查询,使用索引
  • c:无法使用索引(范围查询后的列失效)

2. 函数与计算

-- 索引 INDEX(create_time)
WHERE DATE(create_time) = '2025-11-02'  -- ❌ 索引失效
WHERE create_time >= '2025-11-02 00:00:00' 
  AND create_time < '2025-11-03 00:00:00'  -- ✅ 索引有效

3. 优化器可能的优化

MySQL 8.0+ 引入了索引跳跃扫描(Index Skip Scan),在某些特定场景下可以跳过最左列,但这不是常规优化手段。

四、性能优化建议

1. 索引列顺序设计

  • 区分度高的列放前面:筛选效果好,减少扫描行数
  • 等值查询列放前面:避免范围查询阻断后续列
  • 常用查询列放前面:提高索引利用率

2. 避免冗余索引

-- 已有 INDEX(a, b, c)
-- 则无需再创建 INDEX(a) 和 INDEX(a, b)
-- 因为最左前缀原则已覆盖

3. 合理使用索引覆盖

-- 索引 INDEX(a, b, c)
SELECT a, b, c FROM table WHERE a=1 AND b=2
-- 无需回表,直接从索引获取所有数据

五、面试总结

最左前缀匹配的本质是联合索引在B+树中的排序方式决定的:

  1. 联合索引按照从左到右的列顺序依次排序
  2. 只有最左列在全局范围内有序,后续列只在前面列相同时有序
  3. 因此查询必须从最左列开始,才能利用B+树的有序性进行快速定位
  4. 范围查询会导致后续列无法使用索引(因为范围内后续列无序)
  5. 索引设计时要考虑查询频率、区分度和列的顺序

遵守最左前缀原则是高效使用联合索引的基础,也是MySQL索引优化的核心知识点。