date()等函数导致索引失效,因mysql无法在b+树中直接匹配函数结果;应改用范围查询,如create_time >= '2025-12-27' and create_time
DATE()函数导致索引失效
只要在WHERE里对日期字段套
DATE()、YEAR()、MONTH()这类函数,索引就直接作废。MySQL没法拿函数结果去B+树里查,只能把整列读出来挨个算——500万行表上这么干,12秒起步。常见错误写法:
WHERE DATE(create_time) = '2025-12-27'、WHERE YEAR(order_time) = 2023
- 把函数挪到右边:用范围代替函数,比如
create_time >= '2025-12-27' AND create_time- 如果必须按年/月查,提前建好生成列并加索引,例如:
ALTER TABLE orders ADD COLUMN create_year INT AS (YEAR(create_time)) STORED, ADD INDEX idx_create_year (create_year)- 别信“加了索引就万事大吉”,每次改SQL后一定要跑
EXPLAIN确认key字段非空、type不是ALL隐式类型转换让索引“隐身”
日期字段是
DATETIME或TIMESTAMP,但查询时传了个字符串没带时分秒,或者用了数字,MySQL会悄悄做类型转换,索引就失效了。典型报错不明显,但
EXPLAIN里能看到key为空、Extra出现Using where而不是Using index
- 确保传入值格式严格匹配字段定义:字段是
DATETIME,就传'2025-12-27 14:30:00',别只传'2025-12-27'- 避免用数字比较时间字段,比如
WHERE create_time = 1735286400(Unix时间戳)——这会触发隐式转换- 用
SHOW CREATE TABLE确认字段类型,再对照检查应用层传参类型,尤其注意ORM框架是否自动做了格式化LIKE模糊查时间字段?基本没戏
有人试过
WHERE create_time LIKE '2025-12%'想走索引,结果发现还是全表扫。B+树索引依赖前缀有序性,而LIKE对时间字段本质是字符串匹配,优化器根本不会考虑走索引。
- 时间字段别用
LIKE,这是设计误用。真要按年月查,就用范围查询- 如果业务强依赖“某年某月”的模糊入口,前端应拆成两个参数(
start_time和end_time),后端拼成范围条件- 极少数场景需支持“输入年份自动补全”,可在应用层做预计算,查出年份列表缓存,不依赖数据库模糊匹配
统计信息过期也会让索引“装死”
表数据量突增(比如从10万涨到500万)、批量导入后没更新统计信息,MySQL优化器可能误判“走索引比全表扫描还慢”,主动放弃索引。
实际中,最常被忽略的是函数挪位和统计信息这两块——前者写SQL时图方便随手加
- 执行
ANALYZE TABLE orders强制刷新统计信息,特别是大表变更后- 检查
information_schema.STATISTICS里的CARDINALITY是否合理,若为0或远低于实际唯一值数,说明统计失真- 不要依赖自动分析,生产环境建议在ETL任务末尾加
ANALYZE TABLE步骤DATE(),后者觉得“加了索引就一劳永逸”。但索引不是贴纸,它是活的,得配合查询写法、数据分布、优化器认知一起工作。












