使用DATE()、YEAR()等函数会导致索引失效,应将函数移至条件值侧,改用范围查询如create_time >= '2023-10-01 00:00:00' AND create_time
WHERE里用了DATE()、YEAR()这类函数,索引就废了
MySQL无法对函数计算后的结果复用原始列的B+树索引。比如
DATE(created_at) = '2026-01-15'会让idx_created_at完全失效,EXPLAIN中key显示NULL或退化为index扫描,rows接近全表量级。把函数从列上挪到条件值一侧
核心思路是:保持索引列裸露,用范围边界替代单点函数计算。原语义不变,但能命中索引seek。
DATE(create_time) = '2023-10-01'→ 改成create_time >= '2023-10-01 00:00:00' AND create_timeYEAR(create_time) = 2023→ 改成create_time >= '2023-01-01' AND create_timeLEFT(name, 3) = 'Tom'→ 改成name LIKE 'Tom%'(前提是业务允许前缀匹配)别碰UPPER()/LOWER(),除非你用MySQL 8.0+函数索引
对字符串字段做大小写转换时,
UPPER(name) = 'TOM'必然走全表。MySQL 5.7及更早版本无解;8.0+可建函数索引:ALTER TABLE users ADD INDEX idx_upper_name ((UPPER(name)));但要注意:
(UPPER(name))括号不能少,且该索引只对UPPER()查询生效,普通name = 'tom'仍不走它。时间字段别只建单列索引,联合索引要预留扩展位
如果高频查
user_id+ 时间范围,别只建idx_created_at,优先建覆盖型联合索引:
- 原索引:
KEY idx_created_at(created_at)- 升级建议:
KEY idx_user_created(user_id, created_at)—— 同时支持WHERE user_id = ?和WHERE user_id = ? AND created_at BETWEEN ...- 若还需查状态,直接扩为
KEY idx_user_status_created(user_id, status, created_at),但注意status必须是等值查询,否则created_at列索引失效真正容易被忽略的是:哪怕你把SQL改对了、索引也建对了,如果
SELECT *或返回字段不在索引中,依然要回表——这时候EXPLAIN里Extra会显示Using where; Using index condition,但IO压力没降下来。












