06 慢 SQL 怎么办: 执行计划、优化路径与经典误区
慢 SQL 调优最怕三件事: 凭感觉、看单点、乱加索引。真正高质量的优化,不是把数据库参数背一遍,而是沿着证据做判断。对 PostgreSQL 来说,最核心的证据就是执行计划、统计信息和实际资源消耗。
优化前先分清楚问题属于哪一类
所有慢 SQL 不外乎落在这几类里:
- SQL 本身写法差
- 索引设计不匹配查询路径
- 统计信息失真,规划器选错路
- 单条 SQL 不慢,但并发下互相争资源
- 数据模型本身不适合当前访问模式
如果这一步不先分清,后面很容易越改越乱。
PostgreSQL 调优最有用的工作流
第一步: 把“感觉慢”变成“证据慢”
优先收集:
- 完整 SQL
- 执行频率
- 平均耗时和 P95、P99
- 是否只在高并发时变慢
- 是否和某个时间段、某类参数有关
没有这些信息,调优很容易变成猜测。
第二步: 看真实执行计划
explain (analyze, buffers, verbose)
select ...看计划时,先看下面几件事:
- 走了什么扫描路径
- 估算行数和实际行数差多少
- 排序、哈希、聚合在哪一层
- 哪个节点时间最长
- 是否出现大量循环、回表、临时文件
第三步: 判断问题出在什么层
- 如果估算和实际差很多,优先怀疑统计信息或数据分布。
- 如果扫描行数巨大,优先怀疑索引或查询条件。
- 如果排序、哈希特别重,优先怀疑返回集过大或内存不够。
- 如果单条不慢、高并发变慢,优先怀疑锁、I/O、连接数或热点争抢。
四类常见误区
1. 只要慢就加索引
索引不是万能药。低选择性列、频繁更新列、不匹配查询顺序的列,乱建索引只会增加写入成本和维护负担。
2. 只看“有没有走索引”
走索引不一定快,不走索引也不一定错。关键要看:
- 数据量多大
- 返回行数多少
- 过滤条件选择性如何
- 是否需要排序
3. 忽视类型不一致
字段是 bigint,参数却按文本传;字段是时间戳,查询里却反复做函数转换。这类问题非常常见,而且经常直接影响执行计划。
4. 只优化 SQL,不优化访问模式
有些问题不是某条 SQL 写得差,而是业务本身在做深分页、大范围扫描、热点更新、N+1 查询。这个时候只改 SQL 作用有限,必须改访问策略。
特别值得掌握的几个优化点
复合索引顺序
索引列顺序应该围绕最常见过滤条件和排序需求来设计,不是把常用字段随便拼一起。
部分索引
如果业务大量查询只关心少数状态,比如“未完成订单”,部分索引往往比全量索引更划算。
覆盖索引
如果查询只需要少量字段,可以考虑 include,减少回表成本。
批量化和预计算
有些慢 SQL 的本质不是“算不出来”,而是“每次都现算太贵”。预聚合、物化结果、异步汇总,很多时候比硬调参数更有效。
一个实用判断
真正成熟的 PostgreSQL 调优,不会一上来就说“调大内存”“加几个索引”“升级机器”。它会先回答这几个问题:
- 这条 SQL 到底慢在哪里
- 规划器为什么会这么选
- 这是单条问题、模式问题,还是架构问题
- 最小成本的修复动作是什么
如果你能养成这种调优习惯,大部分慢 SQL 都不会再显得神秘。