WHERE条件中使用函数或表达式会导致索引失效,引发全表扫描;应改用范围查询,如将YEAR(order_date)=2025改为order_date>='2025-01-01' AND order_date

WHERE 条件里用了函数或表达式,索引基本就失效了——这是千万级表查询变慢最常见、也最容易被忽略的原因。
WHERE 子句中函数操作导致全表扫描
比如 SELECT * FROM orders WHERE YEAR(order_date) = 2025,MySQL 无法用 order_date 上的索引,因为 YEAR() 把字段“加工”了,优化器没法直接匹配索引树节点。
- 改成范围查询:
SELECT * FROM orders WHERE order_date >= '2025-01-01' AND order_date - 如果业务真要按年查且频率高,可加生成列 + 索引:
ALTER TABLE orders ADD COLUMN order_year INT AS (YEAR(order_date)) STORED,再建索引CREATE INDEX idx_order_year ON orders(order_year) - 注意:MySQL 8.0+ 才支持函数索引(如
CREATE INDEX idx_order_year ON orders((YEAR(order_date)))),低版本必须走生成列
复合索引顺序错位让索引完全不生效
假设你建了 idx_user_status 索引,字段顺序是 (user_id, status),但查询只写了 WHERE status = 'paid',那这个索引根本不会被用到。
- 复合索引遵循“最左前缀原则”:只有带上
user_id才可能触发索引,例如WHERE user_id = 123 AND status = 'paid' - 如果高频查询是单查
status,那就该单独建INDEX idx_status ON orders(status),而不是指望复合索引“顺带覆盖” - 联合索引字段顺序要按查询频率和区分度调整:高区分度字段(如
user_id)放前面,低区分度字段(如status)放后面
LIKE 前置通配符破坏索引使用
WHERE name LIKE '%abc' 或 WHERE name LIKE '%abc%' 都会强制全表扫描,因为 B+ 树索引只能从左开始匹配。
- 能改写就改写:比如搜索邮箱前缀,用
WHERE email LIKE 'test%@example.com',这样仍可用索引 - 若必须做后缀/中间匹配,别硬扛——考虑
FULLTEXT索引(仅 MyISAM / InnoDB 5.6+ 支持)或接入 Elasticsearch - 小范围兜底方案:加前缀索引(如
CREATE INDEX idx_name_prefix ON users(LEFT(name, 10))),但仅对LIKE 'abc%'有效
NULL 判断和 != 操作让索引直接下线
WHERE status IS NULL 或 WHERE category_id != 5 这类条件,MySQL 通常放弃走索引,尤其在字段 NULL 值比例高或非等值判断时。
- 设计阶段就规避:给
status设默认值(如0或'unknown'),查的时候用= 0 -
!=场景尽量转成范围或IN排除(但注意IN元素过多也会退化,此时用NOT IN更危险,优先考虑反向IN小集合) - 执行前一定看
EXPLAIN:只要type是ALL或key为NULL,说明索引没生效,别信“我建了索引它就该用”
真正卡住性能的,往往不是没建索引,而是建了却因一个括号位置、一个函数调用、一行默认值缺失,让整个索引形同虚设。验证永远比猜测快——每次改完 SQL,先跑一遍 EXPLAIN。










