索引大概率直接失效:因字段与值类型不一致触发隐式转换,使索引列被CAST/CONVERT函数包裹,破坏B+树有序性,优化器弃用索引而全表扫描,EXPLAIN显示type=ALL、key=NULL。

WHERE 条件里字段和值类型不一致,索引大概率直接失效——这不是配置问题,是 MySQL 查询执行器的硬规则。修复不靠调优,只靠让两边类型严格对齐。
varchar 字段传数字常量时索引为何归零?
现象:EXPLAIN 显示 type: ALL、key: NULL、rows 等于全表行数,哪怕该字段明明建了索引。
原因:MySQL 把 phone = 13800138000 解析为 CAST(phone AS UNSIGNED) = 13800138000,索引列被函数包裹,B+Tree 跳查能力彻底失效。
实操建议:
面向 CRM 系统的 MySQL 8 数据库模式设计。当用户需要为客户关系管理(CRM)系统设计、评审、优化或生成数据库模式时,应使用此项技能。
- 所有
VARCHAR/TEXT字段的等值查询,右侧必须用单引号包裹,写成WHERE phone = '13800138000' - MyBatis 中禁用
${}拼接(如WHERE phone = ${phone}),它会吞掉引号;必须用#{} - 前端传参若经 JSON 解析后是数字类型(如
{"phone": 13800138000}),后端 DAO 层要显式转成字符串再传入,不能直接塞进 SQL
int/bigint 字段传字符串时同样危险
现象:EXPLAIN 可能显示 type: index 或 ref,但实际执行仍是全表扫描——旧版本或统计信息不准时,优化器会误判。
原因:MySQL 内部等价于 WHERE CAST(id AS CHAR) = '123',函数作用于索引列,索引无法下推。
实操建议:
-
id是BIGINT,就写WHERE id = 123,不要加引号 - HTTP query 参数(如
?id=123)进后端后,应第一时间转为整型再传给 DAO,而不是留到 SQL 层由 MySQL 隐式转换 - 绝对不要用
CAST(id AS CHAR) = '123'做兜底——这是自废武功
JOIN 关联字段类型或字符集不一致
现象:两表 ON a.code = b.code,但一个字段是 VARCHAR(20) utf8mb4_unicode_ci,另一个是 BIGINT 或 utf8 字符集,EXPLAIN 显示驱动表正常,被驱动表却走 ALL。
原因:MySQL 在关联时会对其中一列做 CAST 或隐式字符集转换,导致索引无法命中。
实操建议:
- 用
SHOW CREATE TABLE table_a和SHOW CREATE TABLE table_b对比字段类型、CHARSET、COLLATE - 首选方案是统一类型与字符集,例如执行
ALTER TABLE t CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci - 临时绕过可用
FORCE INDEX,但只是掩耳盗铃;长期必须改表结构
如何提前暴露隐式转换问题?
MySQL 默认容忍大部分隐式转换,直到慢查询爆发才暴露。主动防御比事后排查更有效。
实操建议:
- 执行
SELECT @@sql_mode,确认是否启用STRICT_TRANS_TABLES;没启用就加上,部分隐式转换会直接报错,逼你改代码 - 在测试环境开启
slow_query_log并设long_query_time = 0,配合pt-query-digest扫描所有查询的EXPLAIN输出,自动标记key: NULL的语句 - DAO 层封装 SQL 构造逻辑时,强制校验参数类型与字段元信息匹配(可通过
DESCRIBE table_name缓存字段类型)

















