mysql函数索引失效因b+树存原始值而非计算结果,如year(create_time)=2023无法走索引;应改写为范围查询,并依explain中type、key_len、using temporary等指标诊断优化。

为什么 WHERE 条件里用了函数索引就失效了
MySQL 无法对表达式结果做索引匹配,比如 WHERE YEAR(create_time) = 2023,即使 create_time 有索引,优化器也无法下推使用。本质是索引 B+ 树里存的是原始值,不是计算后的结果。
实操建议:
- 改写为范围查询:
WHERE create_time >= '2023-01-01' AND create_time - 如果必须按年查,可额外建生成列索引(MySQL 5.7+):
ALTER TABLE t ADD COLUMN y INT AS (YEAR(create_time)) STORED, ADD INDEX idx_y(y) - 避免在索引字段上用
UPPER()、TRIM()、CONCAT()等函数
ORDER BY 和 LIMIT 组合为啥有时走不了索引
当 ORDER BY 字段不在索引最左前缀,或排序方向不一致(如联合索引 (a,b),却写 ORDER BY a ASC, b DESC),MySQL 可能放弃索引排序,改用文件排序(Using filesort)。
实操建议:
- 联合索引顺序要匹配查询顺序,
ORDER BY a,b对应索引(a,b),而非(b,a) -
LIMIT本身不决定是否走索引,但ORDER BY + LIMIT是典型“分页陷阱”,OFFSET很大时即使走了索引,也要跳过大量行 - 深分页优化优先考虑游标法(用上一页最后一条的
id作为下一页条件),而不是依赖OFFSET
什么情况下 SELECT * 会导致索引失效或回表加重
覆盖索引(Covering Index)要求查询字段全部被索引包含。一旦写 SELECT *,而索引只是二级索引(非聚簇索引),就必须回表查主键对应行的完整数据,IO 成倍增加。
实操建议:
- 只查需要的字段,尤其避免在高频查询中
SELECT * - 把常用查询字段加入联合索引末尾,构成覆盖索引,例如查询常要
user_id, status, created_at,可建索引(status, user_id, created_at) - 注意
TEXT/BLOB类型字段不能建索引,且会阻止覆盖索引生效(因为它们不存于索引页)
EXPLAIN 里 type=ALL 和 Extra=Using temporary 分别意味着什么
type=ALL 表示全表扫描,没走任何索引;Extra=Using temporary 表示 MySQL 内部创建了临时表(常见于 GROUP BY、DISTINCT 或某些 JOIN 场景),往往伴随性能陡降。
实操建议:
-
type值从好到差:const≈eq_ref>ref>range>index>ALL;只要看到ALL,就要立刻检查 WHERE 条件是否有可用索引 -
Using temporary出现在 GROUP BY 时,确认分组字段是否已建索引;若用了函数(如GROUP BY DATE(created_at)),同样会触发临时表 -
key_len值比预期小,说明只用了联合索引前缀,后面字段没生效——这时要检查 WHERE 条件是否满足最左前缀原则
EXPLAIN 输出和实际执行路径之间的落差。很多面试题故意在隐式类型转换、字符集不一致、统计信息过期这些地方设坑,光背结论没用。










