为什么 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输出和实际执行路径之间的落差。很多面试题故意在隐式类型转换、字符集不一致、统计信息过期这些地方设坑,光背结论没用。
