加索引查询仍慢是因为索引未被正确使用:b+tree仅支持左前缀匹配和范围后可中断;where条件跳过最左列、order by方向不一致、函数操作或隐式转换均导致失效;覆盖索引需包含select全部字段且避免大字段;like左模糊无法利用索引;order by+limit组合可能因优化器误判或limit过大而弃用索引。

为什么加了索引查询还是慢?
不是所有索引都管用,MySQL 的 B+Tree 索引只对「左前缀匹配」和「范围后可中断」的条件生效。比如你建了联合索引 (a, b, c),但查询写成 WHERE b = 1 AND c = 2,这个索引完全用不上。
常见错误现象:EXPLAIN 显示 type=ALL 或 key=NULL,说明没走索引;或者 rows 值远大于实际返回行数,说明索引扫描效率低。
- 联合索引中,只有最左边的列被用于定位起始位置,中间跳过就断链
-
ORDER BY字段必须和索引顺序一致,且不能混用 ASC/DESC(8.0+ 支持混合,但老版本会失效) - 对索引字段做函数操作(如
WHERE YEAR(create_time) = 2023)或隐式类型转换(WHERE user_id = '123'而user_id是INT),都会导致索引失效
什么时候该用覆盖索引?
覆盖索引不是“更高级的索引”,而是让 SELECT 所需字段全部落在索引 B+Tree 的叶子节点里,避免回表查聚簇索引。它直接减少 I/O,对高并发小字段查询提升明显。
使用场景:分页列表、统计报表中只查几个固定字段,比如 SELECT id, name, status FROM user WHERE status = 1,可以把 (status, id, name) 建成联合索引。
- 注意字段顺序:
WHERE条件字段放前面,SELECT字段放后面,且尽量不包含TEXT/BLOB这类大字段 - 覆盖索引不能解决排序问题,如果
ORDER BY字段不在索引中,仍可能触发Using filesort - 索引越宽,B+Tree 层数越容易增加,插入更新成本也会上升——别为了覆盖把 10 个字段全塞进去
为什么 LIKE '%abc' 无法用索引?
B+Tree 是按字典序存储的有序结构,只能从根节点逐层向下找“起点”。而 LIKE 左模糊('%abc')意味着没有确定的查找起点,必须全量扫描叶子节点,跟全表扫区别不大。
真正能走索引的是右模糊('abc%')或全匹配('abc')。如果你非要搜前缀,又不想改业务逻辑,可以考虑:
- 用倒排索引思路:额外建一列
name_reverse存反转字符串,查LIKE 'cba%'变成查前缀 - 用
FULLTEXT索引配合MATCH ... AGAINST,适合中文需搭配ngram插件 - 确认是否真需要模糊:很多场景其实可以用
IN或枚举值替代LIKE
ORDER BY 和 LIMIT 组合为啥有时不走索引?
MySQL 在优化器判断时,如果预估 LIMIT 取的行数极少,但排序字段又没索引,它可能选择“先取再排序”,而不是“用索引排序后取”。这时候即使有索引,也可能被忽略。
典型例子:SELECT * FROM order WHERE shop_id = 123 ORDER BY create_time DESC LIMIT 10,如果 shop_id 区分度低(比如一个店铺几千单),优化器可能觉得走 shop_id 索引后还要在内存排序太重,干脆全表扫描 + filesort。
- 解决方案是建联合索引
(shop_id, create_time),让过滤+排序一步到位 - 注意方向一致性:如果
ORDER BY create_time DESC,索引也要声明为create_time DESC(MySQL 8.0+ 支持,5.7 及以前默认 ASC,混合方向会失效) -
LIMIT值很大时(比如LIMIT 10000, 20),即使有索引,B+Tree 也要跳过前 10000 行,性能一样差——这时应改用游标分页(WHERE create_time )
真正卡住性能的,往往不是“有没有索引”,而是“索引能不能覆盖整个执行路径”。B+Tree 的结构决定了它擅长定位和范围扫描,但对非顺序、非前缀、非确定起点的操作天然无力。调优时盯着 EXPLAIN 的 key、rows、Extra 三项,比背原理有用得多。











