导读:本期聚焦于白鲨创作的《PostgreSQL慢查询优化:如何用log_executor_stats定位执行器瓶颈?》,敬请观看详情。遇到过这种情况吗:执行计划看起来走了索引,但 SQL 的实际耗时仍然很高,这时问题可能不在规划器,而在执行器内部。PostgreSQL 提供 log_executor_stats 参数,专门控制是否把执行器统计写入服务器日志。开启后,日志会记录排序方法、哈希桶数、分批次数、内存使用以及过滤行数等信息。这些数据来自真实执行过程,比 EXPLAIN 中的估算值更能反映 work_mem 不足、排序溢出、聚合效率差等问题。该参数可以在 postgresql.conf 中全局打开,也可以在单个会话中通过 SET 命令开启,配合 log_min_duration_statement 只采集慢查询日志。需要注意的是,它只负责执行器阶段,不包括解析和规划耗时,因此不能单独代表完整语句时间。当慢查询集中在排序或哈希节点时,log_executor_stats 日志能帮助判断应该调整 work_mem、增加索引还是改写 SQL。本文将介绍参数开启方式、日志字段含义以及结合 auto_explain 的排查思路。

排查 PostgreSQL 慢查询时,执行计划通常是最先看的材料,但执行计划里的成本数据是估算值,真实运行中执行器是否需要磁盘排序、哈希聚合是否分批、内存物化是不是频繁,单看 EXPLAIN 并不能完全确认。log_executor_stats 参数开启后,数据库会把执行器统计写入服务器日志,把执行阶段的排序、哈希、聚合和内存使用情况记录下来。这个参数和 log_statement_stats 不同,后者记录语句整体统计,前者只关注执行器内部,适合已经确认某条 SQL 较慢后进一步定位瓶颈。

PostgreSQL慢查询优化:如何用log_executor_stats定位执行器瓶颈?

要使用这个参数,可以先从会话级验证开始。对于已经抓到慢 SQL 的会话,执行 SET log_executor_stats = on; 就能让当前连接开始输出执行器统计。如果需要全局开启,可以在 postgresql.conf 中写入 log_executor_stats = on,或者使用 ALTER SYSTEM SET log_executor_stats = on; 后执行 SELECT pg_reload_conf(); 让配置生效。实际生产环境一般不建议长期全局开启,因为每条被记录的慢查询都会在日志中额外增加统计块,日志量会明显上升。

一、log_executor_stats 的作用与开启方式

PostgreSQL 提供了一组统计日志参数,包括 log_parser_stats、log_planner_stats、log_executor_stats 和 log_statement_stats。其中 log_statement_stats 会把语法解析、查询规划、执行器三个阶段的统计一次性输出,适合做粗粒度排查;而 log_executor_stats 只输出执行器相关数据,日志更聚焦,适合已经确定瓶颈在执行阶段时使用。两者同时打开时,log_statement_stats 会覆盖掉单阶段的输出,因此如果只需要执行器统计,就不要把 log_statement_stats 一并打开。

查看当前参数状态可以使用下面语句:

SHOW log_executor_stats;

SET log_executor_stats = on;

ALTER SYSTEM SET log_min_duration_statement = '1000ms';
SET log_min_duration_statement = '1000ms';

还可以通过 pg_settings 视图确认参数来源和是否需要重启:

SELECT name, setting, source, pending_restart
FROM pg_settings
WHERE name LIKE 'log_%stats';

log_executor_stats 本身属于普通用户可设置参数,但在全局打开前应当评估日志写入压力。建议先在一个业务低峰窗口内,通过 ALTER SYSTEM SET log_executor_stats = on; 开启,再结合 log_min_duration_statement = '1000ms' 只记录超过 1 秒的语句。这样既能捕获慢查询,又不会把所有快速语句都写进日志。

二、日志字段怎么看,如何定位瓶颈

当开启 log_executor_stats 后,触发慢查询阈值时,数据库日志中会出现类似下面的执行器统计信息:

LOG:  duration: 1842.521 ms  statement: SELECT category, count(*) FROM orders GROUP BY category;
DETAIL:  EXECUTOR STATISTICS
        Sort Method: external merge  Disk: 1024kB
        Buckets: 1024  Batches: 1  Memory Usage: 256kB

这里的 Sort Method: external merge 是关键线索。它说明排序操作无法在 work_mem 分配的内存中完成,已经被溢写到了一块或多块磁盘文件里。外排序的 I/O 成本通常远高于内存排序,日志中如果频繁出现 external merge,基本可以判断 work_mem 设置偏小,或者 SQL 本身要求的排序结果集过大。建议先查看 work_mem 的当前值,再结合排序数据量决定是否调大。

Buckets 和 Batches 则与哈希聚合、哈希连接有关。bucket 数量反映了哈希表的规模,batches 表示哈希操作被分成了几批处理。如果 batches 大于 1,说明内存不足以一次容纳哈希表,数据库需要把部分数据写入临时文件,分批处理。这种情况常见于 GROUP BY 后结果集较大、或连接表数据量很大时。遇到这种日志,可以尝试调大 work_mem,或者通过合适的索引降低进入聚合和哈希连接的数据量。

内存使用统计也能帮助判断执行器是否突破了预设限制。比如日志显示 Memory Usage 接近甚至超过 work_mem,同时伴随磁盘操作,就说明当前查询正处于内存与磁盘交换的临界点。此时不要盲目调大参数,而应优先考虑优化 SQL,例如增加合适的索引、减少排序字段数量、限制聚合前的结果集,才能从源头降低执行器压力。

三、配合 EXPLAIN 和 auto_explain 排查慢查询

单看 log_executor_stats 的日志,只能知道执行器有没有发生磁盘排序、哈希分批,却看不到具体是计划树中的哪个节点触发了这些操作。因此更实用的方式是把它与 EXPLAIN ANALYZE 结合使用。先通过日志确认某条 SQL 存在执行器异常,再回到客户端对同一条 SQL 执行 EXPLAIN (ANALYZE, BUFFERS, TIMING),从计划树中找到 Sort 或 HashAggregate 节点的实际耗时和 I/O 情况。

BEGIN;
SET LOCAL log_executor_stats = on;
SET LOCAL log_min_duration_statement = '1ms';
EXPLAIN (ANALYZE, BUFFERS, TIMING)
SELECT category, count(*)
FROM orders
WHERE created_at >= now() - interval '7 days'
GROUP BY category
ORDER BY count(*) DESC
LIMIT 20;
COMMIT;

对于已经过去的慢查询,可能没有机会再手工执行 EXPLAIN ANALYZE,这时可以借助 auto_explain 扩展。它允许 PostgreSQL 在日志中自动记录超过阈值的语句执行计划,并且可以选择输出实际执行时间、缓冲区使用等信息。把 auto_explain 和 log_executor_stats 同时开启后,一条慢查询在日志中既能看到详细的计划树,也能看到执行器统计,定位效率会高很多。

shared_preload_libraries = 'auto_explain'
auto_explain.log_min_duration = '1000ms'
auto_explain.log_analyze = on
auto_explain.log_buffers = on
log_executor_stats = on

例如一条查询日志显示 external merge 用到了 2GB 磁盘空间,但 EXPLAIN 里的排序估算值并不大,说明实际输入到排序节点的行数被低估了。此时可以通过 auto_explain 输出的计划树确认排序节点上游是否有不合理的过滤条件失效,或者统计信息已经过期。很多情况下,执行 ANALYZE 更新统计信息后,规划器会重新选择更优路径,排序压力也随之下降。

如果确认排序本身无法避免,可以检查表上是否缺少与 ORDER BY 匹配的索引。对于本例中的 ORDER BY count(*) DESC,普通 B-tree 索引无法直接消除聚合后的排序,但可以尝试创建符合 GROUP BY 和 ORDER BY 顺序的覆盖索引,或者使用物化视图提前聚合。对分组字段建立合适索引,也能减少执行器需要处理的行数,从而降低排序和哈希压力。

四、避免把执行器统计当成全部监控

log_executor_stats 的价值在于深挖单条慢 SQL 的执行器行为,但它并不适合作为全局性能监控的日常入口。如果想知道数据库里哪些 SQL 最耗时、调用次数最高,应该优先使用 pg_stat_statements。这个扩展会汇总所有语句的执行次数、总耗时、平均耗时以及缓冲区命中情况,能快速找出最值得优化的目标。

CREATE EXTENSION IF NOT EXISTS pg_stat_statements;

SELECT query, calls, mean_exec_time, max_exec_time
FROM pg_stat_statements
ORDER BY mean_exec_time DESC
LIMIT 10;

定位到具体 SQL 后,再对它开启 log_executor_stats,才是更合理的排查顺序。生产环境中如果长期全局开启该参数,日志系统很快就会堆积大量执行器统计,尤其是高并发写入场景下,日志写入本身也可能成为新的瓶颈。建议在日常监控中保持关闭,只有排查问题时使用 SET LOCAL 只对当前事务生效,或临时调整 log_min_duration_statement 阈值,减少无用日志。

SET log_executor_stats = on;
SET log_min_duration_statement = '2000ms';

有些慢查询看起来执行器统计很干净,没有磁盘排序,也没有哈希分批,但客户端仍然反馈慢。这时需要跳出执行器视角,查看连接是否在等待锁、等待 WAL 写入或等待网络响应。pg_stat_activity 中的 wait_event_type 和 wait_event 可以反映当前会话等待状态,这类问题不会出现在 log_executor_stats 日志里。

SELECT pid, state, wait_event_type, wait_event, query
FROM pg_stat_activity
WHERE state <> 'idle'
  AND backend_type = 'client backend';

总的来说,log_executor_stats 是 PostgreSQL 慢查询优化中一个很实用的定位工具,它把隐藏在总耗时背后的执行器开销拆解出来,让排序溢出、哈希分批、内存不足等问题更容易被发现。正确用法是对整体性能问题先用 pg_stat_statements 做粗筛,再对目标 SQL 临时开启 log_executor_stats,结合 EXPLAIN ANALYZE 或 auto_explain 做精细定位。这样既能快速找到瓶颈,又能避免长期开启带来的日志噪音和额外写入压力。

PostgreSQL慢查询优化log_executor_stats执行器统计修改时间:2026-09-18 13:55:31

免责声明:已尽一切努力确保本网站所含信息的准确性。网站作品多为原创整理与精心创作,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们进行处理Email:chomcom@qq.com。
引用或转载本作品时,请注明当前出处:https://www.ipipp.com/html/0918/58844.html,基于非商业用途的前提下,欢迎转载或二创本作品。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。