现场是这样的。MySQL 5.7 的订单表上 user_id 是 varchar 且有索引,代码里传的是数字,explain 出来 type 是 ALL 扫了三百万行,单条查询四秒,加索引加了个寂寞。

中间走过弯路,把「字段被函数包着用,比如 date(create_time) 等于某天,索引直接失效」当成主因,改完现象没变。

排查方向其实有限,先把可能性排一排。字符串列和数字比较时会把列转成浮点再比,索引顺序完全用不上;两张表的字符集一个是 utf8 一个是 utf8mb4,join 时同样触发转换;转换还会让结果集出现意外匹配,数字开头相同的字符串被当成同一个值。

上来先用 explain 确认是不是扫了全表

mysql -e 'explain select * from orders where user_id=10086\G'

然后看执行后有没有转换告警

mysql -e 'show warnings;'

接着把传参改成字符串再对比执行计划

mysql -e 'explain select * from orders where user_id="10086"\G'

剩下的就是核对列定义与字符集是否一致

mysql -e 'show full columns from orders like "user_id";'

恢复的判定其实很直接。explain 的 type 从 ALL 变成 ref,rows 从三百万降到个位数,同一条查询耗时回到十毫秒以内,告警里不再出现转换提示。

索引有没有用到看的是执行计划,不是看建没建;接口层统一参数类型比事后加索引更省事。拖得越久,排查成本越高,因为现场状态会被后续操作覆盖。

作者 9823189