where子句对索引列用函数会导致索引失效,因索引存储原始值而非计算结果;应改写为裸列在左,如将date(created_at)='2024-01-01'改为created_at>='2024-01-01' and created_at

WHERE 条件里用了函数,索引直接失效
MySQL 在 WHERE 子句中对索引列使用函数(比如 DATE(created_at)、UPPER(name))时,无法走索引——因为索引存的是原始值,不是函数计算后的结果。
常见错误现象:EXPLAIN 显示 type=ALL 或 key=NULL,哪怕字段明明建了索引。
- 改写方式:把函数移到右边,让左边保持裸列。例如把
WHERE DATE(created_at) = '2024-01-01'改成WHERE created_at >= '2024-01-01' AND created_at - 如果必须大小写不敏感查询,优先用
COLLATE utf8mb4_0900_as_cs或建函数索引(MySQL 8.0+):CREATE INDEX idx_name_lower ON users ((LOWER(name))) - 注意
LIKE开头带通配符(LIKE '%abc')也等价于“对列做前缀截断”,同样无法用索引
联合索引顺序错,等于白建
联合索引 (a, b, c) 不是三个单列索引的叠加,它天然有序:先按 a 排,a 相同再按 b,依此类推。所以查询条件必须满足“最左前缀原则”才能命中。
使用场景:高频查询是 WHERE a = ? AND c = ?,但没用 b —— 这时 (a,b,c) 索引只用到 a,c 被跳过,效果和只有 a 索引一样。
- 判断顺序的核心依据是查询频率和过滤性:
a区分度低(比如status只有 3 个值),b区分度高(比如user_id),那(b, a)往往比(a, b)更有效 -
ORDER BY字段如果也在联合索引里,必须和索引顺序一致且不能混用 ASC/DESC(MySQL 8.0 前不支持混合排序索引) - 不要为了“覆盖所有组合”建一堆联合索引,优先合并:比如已有
(a,b)和(a,b,c),通常只需留后者
TEXT/BLOB 列上建普通索引会报错或截断
在 TEXT、BLOB 类型字段上直接写 INDEX(col),MySQL 会报错 ERROR 1170 (42000): BLOB/TEXT column 'col' used in key specification without a key length;即使加了长度(如 INDEX(col(255))),也只是对前 N 字节建索引,模糊匹配容易误判。
性能影响:前缀索引无法支持 ORDER BY 或 GROUP BY,也不能用于覆盖索引(SELECT * 仍要回表)。
- 能转成
VARCHAR就转,尤其是内容长度可预期(如 URL、邮箱、标题) - 真要搜全文,用
FULLTEXT索引 +MATCH ... AGAINST,别硬扛LIKE - 如果只是校验唯一性(比如长 token),考虑哈希后存进新列并建索引:
ALTER TABLE t ADD COLUMN token_sha256 CHAR(64) AS (SHA2(token, 256)) STORED,再对token_sha256建唯一索引
UPDATE/DELETE 语句没加 WHERE,或者 WHERE 不走索引
没有索引的 WHERE 条件,在大表上执行 UPDATE 或 DELETE 会锁全表(或全索引),阻塞其他读写,还可能触发慢查询告警甚至被 kill。
容易踩的坑:开发习惯性写 UPDATE t SET status=1 WHERE id IN (1,2,3),但 id 没建索引;或者线上日志里看到 Rows_examined 是几百万,但业务只改几条。
- 上线前必须确认 DML 的
WHERE是否命中索引,用EXPLAIN FORMAT=TREE(MySQL 8.0+)看执行路径最直观 - 批量更新优先走主键或唯一索引;避免用非唯一字段(如
email)做条件,除非该字段已建索引 - 测试环境跑 DML 前加
SELECT COUNT(*)验证条件范围,防止误操作
EXPLAIN 里安静地显示 key=NULL,而你还在查应用层日志。











