在复杂的业务系统中,报表统计和数据聚合功能往往对数据库性能有着较高的要求。在某次市级心理预警报表的数据统计任务中,当系统管理员触发查询指令后,后端的 MySQL 主库 CPU 占用率瞬间飙升至满载,原本预估在 1 秒内响应的报表请求,实际耗时超过了 20 秒。
针对这类突发的性能下降问题,技术人员通常会借助 MySQL 慢查询日志(Slow Query Log)来进行问题定位。在此次排查中,慢查询日志记录下了一条耗时较长的联表查询 SQL:
“`text
Time: 2026-07-15T09:00:01.123456Z
Querytime: 21.500312 Locktime: 0.000102 Rowssent: 50 Rowsexamined: 5000000
SELECT s.schoolname, COUNT(r.id) as dangercount
FROM student s
LEFT JOIN psyreport r ON s.studentid = r.student_id
WHERE r.alertlevel = ‘RED’ AND s.cityid = 1001
GROUP BY s.school_name;
“`
从日志各项指标来看,Querytime 达到了 21.5 秒,而更值得注意的是 Rowsexamined 字段,该字段显示 MySQL 在执行这条查询时,全表扫描和遍历了大约 500 万行数据,最终只返回了 50 行结果。这表明底层的检索过程中极有可能出现了全表扫描或者嵌套循环过程中的索引失效问题。
为进一步分析该语句在底层的执行轨迹,我们在该 SQL 语句前添加 EXPLAIN 关键字,以获取 MySQL 生成的执行计划。
“`sql
EXPLAIN SELECT s.school_name …
“`
在返回的执行计划中,关于 psyreport(心理报告数据表)的分析结果给出了问题答案。该表的 type 列显示为 ALL,代表进行了全表扫描;而 Extra 列包含了 Using join buffer (Block Nested Loop)。这说明在执行 LEFT JOIN 联表查询时,由于被驱动表 psyreport 在关联字段和条件过滤字段上没有合适的索引,MySQL 不得不在内存缓冲中进行块嵌套循环,对主表提取出的每一行记录,都要去被驱动表中做全表遍历匹配。面对数据量达数百万级别的报告表,这种嵌套遍历会消耗大量的 CPU 算力与 I/O 资源。
明确了性能瓶颈后,解决方案是在 psy_report 表上建立有针对性的联合索引。
“`sql
— 添加联合索引,同时满足关联字段与条件过滤字段的查询需要
ALTER TABLE psyreport ADD INDEX idxstualert (studentid, alert_level);
“`
完成该复合索引的建立后,再次执行相同的报表查询语句,耗时大幅度缩短至 0.08 秒左右。此时重新运行 EXPLAIN 分析,psy_report 表的检索类型从 ALL 变为了 ref,表明已经能够正常使用索引匹配,扫描行数也降至合理范围。数据库的 CPU 负载也随即恢复平稳状态。
在橙星云的后端架构演进过程中,类似的数据规模增长带来的查询效率下降是不可避免的挑战之一。研发团队通过完善慢查询监控机制,并定期依据业务使用场景核查和优化索引结构,能够有效预防此类由于索引缺失引发的资源挤兑现象。对于经常涉及多表连接的统计业务,通过执行计划透视底层物理扫描逻辑,并合理建立联合索引,是保障系统稳定运行和报表高效输出的关键手段。
