建立了 abc 联合索引,where a = ? and b = ? order by c 能命中索引吗?
解答这个问题之前我的建议是自己写个代码测试一下,在面试被问到的时候 回答更有底气。
-- 想象中的表结构
CREATE TABLE orders (user_id INT, -- 用户IDshop_id INT, -- 店铺ID order_time DATETIME, -- 下单时间amount DECIMAL(10,2)
);-- 创建联合索引:(user_id, shop_id, order_time)
CREATE INDEX idx_user_shop_time ON orders (user_id, shop_id, order_time);
所以先创建一个表,并且创建联合索引
如果查询有a、b、c并且按照顺序
explain SELECT * FROM orders
WHERE user_id = 1001 AND shop_id = 2005
ORDER BY order_time;
可以看到是走了索引的。
如果查询只有b和c
explain SELECT * FROM orders
WHERE shop_id = 2005 AND order_time = '2025-01-01 10:00:00';
显而易见,如果没有遵循最左匹配 那么索引会失效 不会走索引
a、b、c都有 但是顺序是乱的
SELECT * FROM orders
WHERE order_time = '2025-01-01 10:00:00'AND user_id = 1001AND shop_id = 2005;
能看出查询顺序是c、a、b
但还是走了索引,这是为什么呢 不是并没有满足最左前缀匹配吗
MySQL 查询优化器(Optimizer)会重写查询条件的顺序
- 它知道索引是
(user_id, shop_id, order_time)
- 它会自动将等值条件按索引顺序排列:先
user_id=1001
,再shop_id=2005
,最后order_time=...
- 所以实际执行时,等价于:
WHERE user_id = 1001 AND shop_id = 2005 AND order_time = '2025-01-01 10:00:00'
- 它知道索引是
满足最左前缀原则
user_id
是等值条件,命中索引第一列 ✅shop_id
是等值条件,命中第二列 ✅order_time
是等值条件,命中第三列 ✅- 所有字段都在索引中,可以快速定位唯一一行(或少数几行)
结果:高效走索引,使用
ref
或const
类型访问,无需全表扫描
只有a、b
explain SELECT * FROM orders
WHERE user_id = 1001AND shop_id = 2005;
也是走了索引的,并且效率更高
原因分析:
- 索引是
(user_id, shop_id, order_time)
- 查询条件使用了前两个字段:
user_id=?
和shop_id=?
- 完全符合最左前缀原则(Leftmost Prefix)
- 数据库可以:
- 快速定位到
user_id=1001
的所有记录 - 在这些记录中,再快速筛选出
shop_id=2005
的子集 - 找到后直接回表获取
amount
等字段
- 快速定位到
只有a、c
explain SELECT * FROM orders
WHERE user_id = 1001AND order_time = '2025-01-01 10:00:00';
虽然使用了联合索引,但只在
a
字段上进行了高效定位,而c
字段的条件无法直接通过索引定位(因为跳过了中间的b
字段),因此数据库需要对a=...
对应的索引范围进行扫描,并逐行过滤c
的值,无法完全利用索引的有序性
原因分析:
- 索引顺序是:
user_id → shop_id → order_time
- 查询条件是:
user_id=?
和order_time=?
- 虽然
user_id
是最左字段,可以使用 ✅ - 但跳过了中间的
shop_id
,直接用了order_time
这会导致:只能使用索引的前缀 user_id
,而 order_time
无法用于索引的“范围匹配”或“等值定位”