## 案例
运营后台一个新上线的报表页面,按手机号后四位查用户列表。上线当天下午,全站响应变慢,客服开始接到投诉。数据库 CPU 飙高,慢查询日志里一条语句刷屏:对用户表几百万行数据的全表扫描,每条耗时好几秒,而页面上翻一次页就是一串这样的查询。网站的核心交易链路虽没直接用这条 SQL,但共享同一个数据库实例,IO 与缓冲池被报表查询冲刷,全体查询跟着变慢。
## 根因分析
问题出在一个没建索引的查询条件。用户表的索引只有主键与另两个字段,"手机号后四位"这个新条件不在任何索引里,优化器只能全表扫描:几百万行逐行比对。单独看每次几秒不算灾难,灾难在放大效应:翻页加载多请求并发、缓存命中率下降导致更多查询落盘、缓冲池的热数据被扫描挤出去,核心业务的最简单查询也开始变慢——一颗小石子引发整片池塘浑浊。报表类功能在业务低峰无所谓,高峰时段就成了公害。
## 修复过程
第一步止血:临时下线报表入口,数据库负载立刻回落,核心链路恢复。第二步分析,用 EXPLAIN 看执行计划:
sql EXPLAIN SELECT * FROM users WHERE mobile_suffix = '6789';
type 列显示 ALL、rows 列几百万,坐实全表扫描。第三步建索引:
sql ALTER TABLE users ADD INDEX idx_mobile_suffix (mobile_suffix);
再跑 EXPLAIN,type 变为 ref,rows 降到个位数到百位数级。第四步复测:报表页面恢复上线,单次查询毫秒级返回,慢查询日志安静,全站延迟回落到基线。
## 复盘要点
新查询条件必须评估索引,上线前的 SQL 审查要过一遍 EXPLAIN,这条纪律成本最低收益最大。善后还做了两件事:把"后四位"条件改为基于已有的完整手机号索引做前缀匹配的改写方案,减少冗余索引;报表类查询限流并错峰,非核心流量不得与交易链路抢资源。慢查询日志常态化开启,顺云控制台的数据库所在机器监控配好 CPU 与 IO 告警,下次再有全表扫描冒头,第一时间就能看见。