一张八百万行的用户表,uid 上有普通索引,explain 出来却是 type ALL,加强制索引才走,MySQL 版本 5.7.36,每天凌晨跑统计。

一开始怀疑是「索引选择性太差,同一个值占了九成行」,查了一圈,方向不对。

真正麻烦的地方在于,几个原因的现象几乎一样。统计信息陈旧,优化器以为走索引比全表扫更贵。少数情况下是查询要回表的列太多,优化器按覆盖成本算亏了。

少数情况下是字符集不一致,索引列被隐式转换掉。

动手时先看优化器估算的行数和过滤比例

mysql -e "explain select * from users where uid=123456\G"

刷新统计信息并看基数

mysql -e "analyze table users; show index from users;"

接着对比强制索引前后的实际耗时

mysql -e "explain select * from users force index(idx_uid) where uid=123456\G"

剩下的就是确认表与连接字符集一致

mysql -e "show create table users\G" | grep -i charset

恢复的判定其实很直接。analyze 之后访问类型从 ALL 变成 ref,扫描行数从八百万降到个位数,接口耗时从两秒降到二十毫秒。

提醒一点。优化器选错索引先用 analyze 排除统计问题,再考虑强制索引;长期靠 force index 会掩盖数据分布本身的问题。这类问题最好一次改到位,反复小改反而容易把原因搅混。

作者 9823189