本文目录
索引不是越多越好,优化结论要覆盖读、写和锁等待
先按执行次数、累计耗时和业务影响确定慢 SQL 优先级,再结合代表性参数、数据分布与实际执行计划定位扫描、连接、排序或锁等待。组合索引应服务具体筛选与排序路径,不能照着字段清单全部建立;深分页、重复查询、长事务和批量导出往往需要改变访问方式。验收时固定数据与负载,比较查询延迟、扫描范围和资源,同时检查写入、存储、索引维护与上线建索引的影响。
不同数据库症状对应不同处理
| 症状 | 先核对的证据 | 避免直接采取 |
|---|---|---|
| 扫描过多 | 实际返回行与扫描行、过滤条件和数据分布是否匹配 | 只看到全表扫描就强制索引,忽略大比例返回场景 |
| 排序或分页慢 | 排序键、分页深度、结果大小与临时文件 | 继续扩大内存,却不限制扫描与返回范围 |
| 频繁短查询 | 一次业务动作的查询数量和相同数据重复读取 | 只优化单条 SQL,忽略 N+1 和网络往返 |
| 等待锁或连接 | 事务持有时间、阻塞链和连接占用 | 给所有表增加索引,未解决真正等待来源 |
适用边界:区分数据库慢与整个接口慢
这份方法适用于关系型数据库的业务查询与写入,执行计划示例依据 PostgreSQL 官方文档。MySQL、SQL Server 等引擎的索引能力和在线变更语义不同,实际命令应按项目版本核对。
接口总耗时可能包含连接等待、数据库执行、结果传输和应用序列化。数据库单次查询很快,但多次串行请求或返回大结果仍会慢。先拆分阶段,确认优化目标是交易查询、后台报表还是批量处理;报表的长查询不应占用与交易相同的资源预算。
查询返回的数据还要验证权限和完整性。优化筛选、连接或分页时,容易因条件位置和排序改变出现漏行、重复行或租户边界错误。性能比较前先用代表性数据核对结果集合;游标分页应包含能够稳定打破并列排序的字段,避免跨页数据重复或遗漏。
- 记录请求阶段耗时和单次动作的查询次数。
- 明确结果行数、字段大小、数据库版本与业务等待预算。
按累计负担找出值得优先改的查询
最慢的一条 SQL 不一定造成最大负担。高频中等耗时查询可能消耗更多数据库时间;低频导出则可能在活动期间抢占 I/O,优先级应同时考虑频率、资源和业务影响。
按归一化 SQL 分组收集执行次数、总时间、平均与高分位耗时,再选代表性参数检查数据倾斜。某个租户只有少量记录,另一个租户有大量历史数据,同一语句可能采用不同计划。避免在日志中直接暴露敏感参数,使用脱敏样本保存可复现条件。
- 分别标注在线交易、高频查询和批量任务,建立优化优先级。
- 保留峰值窗口与常规窗口的参数分布,避免只测最小结果集。
读执行计划,先分清估算与实际结果
执行计划可揭示扫描、连接与排序方式,但估算成本不是毫秒,估算行数也不等于实际返回。重点核对行数偏差、循环次数、过滤范围、缓冲区与排序落盘,再判断统计信息或访问路径问题。
PostgreSQL 的 EXPLAIN ANALYZE 会真正执行语句,因此数据修改语句不能在生产随意运行;回滚也未必抵消序列或外部副作用。应优先在隔离数据中分析,生产只做经评估的只读诊断。小数据全表扫描可能合理,执行计划的目标是说明成本来源,而不是让所有查询出现索引扫描。
- 比较估算行数与实际行数,检查参数倾斜和统计信息是否陈旧。
- 对多层循环、巨大中间结果、落盘排序与锁等待分别提出假设。
让组合索引对应筛选、排序与返回范围
常见访问路径是某租户内按状态和时间查询,并按时间与唯一标识分页。组合索引设计需要同时考虑等值筛选、范围条件、排序稳定性与选择性,字段顺序不能靠“最常用字段”一句话决定。
PostgreSQL 文档指出 B-tree 多列索引通常在前导列约束下更有效,具体可用路径还取决于版本与优化器。不要把某一引擎的经验写成所有数据库的绝对规则。覆盖字段、部分索引等能力也有空间与维护代价,建立后需用实际查询和写入负载验证,冗余索引应评估后再清理。
- 先列出需要支持的具体查询形态,再设计少量候选索引。
- 记录索引尺寸、写入耗时、维护方式以及上线构建影响。
实施步骤:减少工作量,再评估缓存或扩容
依次完成查询分组、阶段定位、计划分析、SQL 与索引候选验证、灰度发布和回退准备。先限制返回字段、修复重复查询、控制事务长度,再判断缓存和读副本是否适合业务一致性。
深分页若不断跳过历史行,可以考虑基于稳定排序键的游标分页;它改变跳页能力,产品需接受这一边界。长事务内不要穿插不可控外部调用,批量操作按业务一致性选择分批方式。新增索引的在线能力、锁影响和失败清理步骤必须按引擎核对,避免优化发布本身阻塞交易。
- 每次只验证一个主要变量,保留相同参数与数据的前后计划。
- 为分页、批量任务和事务改变补充结果集合与状态一致性检查。
验收:查询变快同时不能把写入变慢失控
在代表性数据与并发下比较目标查询、写入和资源的变化,不能只拿开发机一次执行截图。索引优化需要证明收益可持续,并检查新索引的存储、复制和维护成本。
重复执行可以降低偶然噪声,但预热与冷读结果需分开解释。验收包含热门和冷门参数、较深页面、写入高峰以及报表叠加。若查询耗时改善但连接等待仍占大头,应继续解决连接占用和事务,而不是认定接口已经全部优化完成。
处理历史大表时,还要检查统计信息更新、长期事务与维护任务的时段。新索引上线后持续观察实际使用与写入代价,再决定是否删除旧索引;索引可能同时支持约束和其他低频关键路径,不能只因某个统计窗口未使用就立即删除。
- 保存目标 SQL 的计划、参数类别、数据量与测试窗口。
- 验证灰度后错误、锁等待和写入延迟没有新增不可接受影响。
需求讨论中的问题
全表扫描一定需要增加索引吗?
不一定。表很小或需要返回大量数据时,全表扫描可能更合适。先看扫描范围、实际成本、数据分布和业务查询形态,再用候选索引比较。
所有筛选字段都建索引会更快吗?
索引会占空间并增加写入与维护负担,而且优化器未必使用。应围绕高价值查询设计索引,验证组合关系与写入成本,避免重复或无用索引。
可以在生产直接运行 EXPLAIN ANALYZE 吗?
它会真正执行 SQL。需要先判断语句副作用、资源与锁影响,数据修改和高成本语句优先在隔离环境分析;事务回滚也不能保证撤销所有外部副作用。
慢 SQL 优化验收清单
可逐项勾选,辅助本次阅读;刷新页面后不保留。
参考资料
技术概念可进一步阅读以下资料;文中的业务场景与实施清单用于方案讨论。
- PostgreSQL:Using EXPLAIN
核验计划估算、实际行数及 EXPLAIN ANALYZE 实际执行语句的边界。
- PostgreSQL:Multicolumn Indexes
核验多列 B-tree 的前导列约束和版本相关优化;具体索引需结合项目验证。
需要评估现有系统的性能瓶颈?
提供关键业务路径、真实峰值、服务器与数据库配置,以及现有压测或故障记录,月弦科技可据此梳理优化顺序、实施范围和可验证的验收条件。