WHERE 明明有索引却全表扫描,经常是列类型与常量类型不一致触发隐式转换。

机制说明

当比较两侧类型不同时,MySQL 按转换规则把一侧转为另一侧。若对索引列做函数或转换,优化器往往无法使用该索引,因为索引存的是原始值顺序。

典型场景

1
2
3
4
5
6
7
8
9
-- phone 为 VARCHAR,常量写数字 → 列被转数字,索引失效
SELECT * FROM user WHERE phone = 13800138000;
-- 正确写法
SELECT * FROM user WHERE phone = '13800138000';

-- 对列做函数同样失效
SELECT * FROM orders WHERE DATE(created_at) = '2026-01-01';
-- 改为范围查询
SELECT * FROM orders WHERE created_at >= '2026-01-01' AND created_at < '2026-01-02';

排查步骤

EXPLAIN 看 type=ALL 且 key=NULL;SHOW WARNINGS 有时能看到 cast 提示;核对列类型与应用传参类型。

实践建议

ORM 参数绑定保持类型一致;时间字段用范围而非函数包裹列;联合索引注意字符集/collation 一致。

字符集转换链

utf8mb4 列与 utf8 列比较可能触发 convert(col using utf8mb4),破坏索引。统一 schema charset 是一劳永逸方案。

工具辅助

pt-query-digest 聚合慢 SQL;use index 仅作调试,生产别硬 hint 除非验证过。

实践复习清单

Code review 检查 WHERE 类型;EXPLAIN 看到 ALL 先查 cast;时间查询改范围;phone/email 等字符串列禁止数字常量;统一 charset;单元测试覆盖边界类型。

常见坑

  • utf8 与 utf8mb4 混用导致转换。
  • 以为 LIKE 'abc%'= 'abc' 走索引方式相同(前缀匹配才可用 B+ 树)。

总结与自测

说出三条隐式转换导致失效的 SQL 写法;如何验证 cast;时间函数包裹列如何改写;字符集混用如何发现。Review 团队慢 SQL 时,这一项应排在最前几位检查。

原理延伸

InnoDB 的核心竞争力在于把 B+ 树索引、缓冲池、redo/undo 与 MVCC 组合成可预测的 OLTP 引擎。读路径尽量走缓冲池与覆盖索引,写路径则要在行锁、日志刷盘与复制延迟之间取平衡。排查问题时不要只盯单条 SQL,还要看事务边界、锁等待、buffer pool 命中率与磁盘 fsync 延迟。性能优化永远遵循「先正确、再可测、后优化」:用慢日志和 EXPLAIN 定位瓶颈,用规范设计减少回表与锁竞争,用架构手段(读写分离、缓存、分片)承接流量,而不是在未测量前堆参数。