把慢日志、执行计划和数据分布结合起来定位瓶颈。
核心思路
先确认慢是偶发还是稳定,再查看扫描行数、返回行数和锁等待。避免在索引列上做隐式类型转换或函数运算。
落地步骤
优化目标不只是单次查询更快,也要降低并发下的资源消耗。调整 SQL 或索引后应回归写入性能与边界结果。
验证清单
实施后不要只确认“能运行”,还应检查错误率、延迟、资源占用和异常路径。把验证命令、预期结果和回滚方式写进变更记录,后续维护会轻松很多。
小结
好的工程实践不是堆叠工具,而是用清晰边界、可观察结果和可逆变更降低长期成本。先解决最重要的问题,再逐步自动化。
可执行实践
启用受控慢日志并按总耗时、平均耗时和扫描行数排序。使用 EXPLAIN ANALYZE 检查访问类型、实际行数、回表与排序,确认字段类型避免隐式转换。
验证与退出条件
在副本或压测环境验证新索引对写入和磁盘的影响。线上变更观察锁等待、Buffer Pool 和复制延迟,保留旧索引到稳定期结束。
实践场景
以“处理高峰期出现的 MySQL 慢查询”为例。实施前先保存当前版本、配置和关键指标基线,写明成功条件与停止条件;涉及生产数据或外部写入时,先在隔离环境验证。
分步实施
从慢日志确认指纹与频率,用 EXPLAIN ANALYZE 检查访问路径;先减少扫描与返回列,再考虑索引和分页方式。每完成一步都记录可观察结果,不把命令返回零等同于业务正确。
故障演练
用接近生产的数据量测试选择性差、参数极端和锁等待,避免小表上优化得出错误结论。演练应使用可清理的测试数据,并验证告警、日志和恢复路径,而不只是确认系统最终又能访问。
观测与回滚
观察 P95、rows examined、锁等待和写放大;回滚索引或 SQL 前确认兼容旧应用版本。变更单中应写清负责人、观察窗口、阈值和回滚触发器,避免故障发生后再临时争论。
完成后把使用的版本、关键命令、观测窗口和结果写入变更记录。没有验证证据的“已完成”不能作为上线结论。
第二轮实践复核
可复用交付物: 慢查询指纹、真实执行计划和数据分布说明。复用时先固定软件版本、依赖锁文件、测试数据规模和运行环境;文章中的命令包含占位符,必须替换成自己的非生产目标后再执行。
最低验证: EXPLAIN ANALYZE SELECT ...;
执行记录要保存命令、退出码、关键输出、观察窗口和回滚结果。若验证会产生写入,先使用隔离环境或 dry-run;任何失败都不能通过扩大权限、关闭校验或跳过测试来“解决”。
完整实战:订单列表从 1.8 秒降到预算内
查询按用户、状态和时间筛选并倒序分页:
1 | SELECT id, status, total_cent, created_at |
候选复合索引是 (tenant_id, status, created_at, id),等值条件在前、范围和排序字段在后。但索引设计不能只看这一条查询:需要统计字段选择性、其他常用路径、写入放大和索引体积,再在同数据分布的环境验证执行计划。
1 | EXPLAIN ANALYZE |
关注实际扫描行数、循环次数、排序/临时表、估算与实际偏差,而不是只看到“使用了某个索引”就结束。若返回 50 行却扫描几十万行,索引或谓词仍有问题。
安全变更顺序
| 阶段 | 动作 | 退出条件 |
|---|---|---|
| 基线 | 保存查询指纹、P95、调用量、计划 | 能稳定复现 |
| 候选 | 在影子数据创建索引 | 计划改善且写入成本可接受 |
| 灰度 | 低峰在线变更,限制资源 | 复制延迟、锁等待未越线 |
| 观察 | 对比读写延迟和慢查询 | 完整业务周期无回退 |
| 清理 | 评估重复/无用索引 | 有使用证据和回滚窗口 |
深分页不要无限使用 OFFSET,它仍需扫描并丢弃前面的行。使用稳定排序的 seek/cursor 分页,并包含并列字段 id。应用层同时限制最大页大小和时间范围,避免一个合法请求变成全表工作。
故障演练包括冷缓存、热点租户、统计信息过期、并发写入和复制库延迟。上线后观察查询 P95/P99、扫描/返回比、buffer pool 命中、锁等待、复制延迟与写入耗时。出现回归时先切回旧查询路径;删除索引是单独的后续动作,不要和应用回滚绑在一起。
一手资料
资料与版本边界复核日期:2026-08-22。工具行为可能演进,实施前以锁定版本对应的官方文档为准。
评论