news 2026/10/8 6:43:40

PostgreSQL 中 EXPLAIN 与 EXPLAIN ANALYZE 的区别:一个会执行查询,一个不会

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
PostgreSQL 中 EXPLAIN 与 EXPLAIN ANALYZE 的区别:一个会执行查询,一个不会
  • 文档
  • 教程
  • 知识库

【免费下载链接】til

:memo: Today I Learned

项目地址:https://gitcode.com/gh_mirrors/ti/til
点击查看免费下载

EXPLAIN是 PostgreSQL 中探查查询执行计划与性能表现的核心命令,而EXPLAIN ANALYZE常被当作它的"加强版"在对话中混用。本篇基于 til 仓库中 postgres/difference-between-explain-and-explain-analyze.md 的实战记录,厘清两者的本质差异——EXPLAIN ANALYZE会真正执行查询,EXPLAIN不会,并围绕 INSERT/UPDATE/DELETE 等写操作给出可复现的验证示例,帮助你安全、正确地用它们定位查询瓶颈。

核心区别:执行与不执行

EXPLAIN语句可以让你对一条查询的性能表现获得洞察。日常交流中,EXPLAIN和EXPLAIN ANALYZE经常被混着叫,但它们都能用来探查查询如何执行的同时,有一个关键区别必须清楚:

  • EXPLAIN ANALYZE会执行查询;
  • EXPLAIN不会执行查询。

对于SELECT查询,这个区别可能感觉并不重要——毕竟 SELECT 本身没有副作用。但对于INSERT、UPDATE和DELETE这类写语句,你就必须搞清楚自己用的是哪一个了,因为两者的数据影响完全不同。

仅输出成本估算:EXPLAIN

EXPLAIN只输出规划器基于统计信息生成的成本估算(cost estimates),不触碰真实数据。下面针对books表执行一条 INSERT 的 EXPLAIN:

> explain insert into books (title, author) values ('Fledgling', 'Octavia Butler'); QUERY PLAN ---------------------------------------------------- Insert on books (cost=0.00..0.01 rows=1 width=76) -> Result (cost=0.00..0.01 rows=1 width=76) > select count(*) from books; count ------- 0

可以看到,执行完EXPLAIN insert ...之后再次select count(*),books表依然是0 行——查询并没有真正执行,你得到的只是这条 INSERT 语句的成本估算值。cost=0.00..0.01中的起始成本与总成本、rows=1的估算行数、width=76的估算元组宽度,全部来自规划器的代价模型与表统计信息。

输出实际执行数据:EXPLAIN ANALYZE

EXPLAIN ANALYZE会真实执行这条 INSERT,并输出成本估算 + 实际执行数据(actual numbers):

> explain analyze insert into books (title, author) values ('Fledgling', 'Octavia Butler'); QUERY PLAN ---------------------------------------------------------------------------------------------- Insert on books (cost=0.00..0.01 rows=1 width=76) (actual time=0.285..0.285 rows=0 loops=1) -> Result (cost=0.00..0.01 rows=1 width=76) (actual time=0.012..0.012 rows=1 loops=1) Planning time: 0.021 ms Execution time: 0.309 ms > select count(*) from books; count ------- 1

对照两次输出可以看到三组关键差异:

  1. 多出actual time/rows/loops字段:每个计划节点都附带了真实耗时(单位毫秒)、实际行数与循环次数;
  2. 多出Planning time与Execution time两行:分别对应规划阶段与实际执行阶段的耗时;
  3. 副作用确实发生:执行完EXPLAIN ANALYZE insert ...之后,books表中真实多出了一行(count 从 0 变为 1)。

也就是说,EXPLAIN ANALYZE给你的是"预测 + 实测"的完整画像,但代价是它会像正常执行语句一样修改数据。同理,EXPLAIN ANALYZE用在UPDATE、DELETE上也会真实改动或删除数据,必须谨慎。

什么时候用哪一个

基于上述差异,可以给出明确的选用原则:

场景推荐命令原因
只读查询(SELECT)调优EXPLAIN ANALYZE无副作用,且能拿到实际耗时与行数,判断是否与估算严重偏离
写操作(INSERT/UPDATE/DELETE)排查先用EXPLAIN只读执行计划,不产生任何数据改动
需要验证写操作的真实代价EXPLAIN ANALYZE(配合事务回滚)确认实际耗时,但要接受数据会被修改

对于写操作,如果想安全地获得真实执行数据,一个常用技巧是把EXPLAIN ANALYZE放在事务中执行并回滚:

begin; explain analyze delete from books where title = 'Fledgling'; rollback;

这样既能拿到真实执行指标,又不会让数据被永久改动(注意:即便回滚,语句仍会真实执行、占用锁与资源,行为与正常 DML 一致)。

从仓库延伸:EXPLAIN 的实战配套用法

这条 TIL 记录是 til 仓库 PostgreSQL 分类下查询性能探查系列的一部分,仓库内还有若干与之直接配套的实战笔记,组合使用可以覆盖从"看懂计划"到"量化对比"的完整链路。

多种输出格式:TEXT / JSON / YAML / XML

EXPLAIN(或EXPLAIN ANALYZE)默认输出为供人阅读的TEXT格式。在 postgres/output-explain-query-plan-in-different-formats.md 中展示了如何切换到程序可解析的标准化格式,例如:

> explain (analyze, format json) select title from books where created_at > now() - '1 year'::interval; QUERY PLAN ---------------------------------------------------------------- [ + { + "Plan": { + "Node Type": "Seq Scan", + "Parallel Aware": false, + "Async Capable": false, + "Relation Name": "books", + "Alias": "books", + "Startup Cost": 0.00, + "Total Cost": 1.28, + "Plan Rows": 5, + "Plan Width": 32, + "Actual Startup Time": 0.008, + "Actual Total Time": 0.014, + "Actual Rows": 22, + "Actual Loops": 1, + "Filter": "(created_at > (now() - '1 year'::interval))",+ "Rows Removed by Filter": 0 + }, + "Planning Time": 0.050, + "Triggers": [ + ], + "Execution Time": 0.023 + } + ] (1 row)

explain (analyze, format json)中的format选项可取值TEXT(默认)、JSON、YAML、XML,适合接入监控、自动化分析或生成可视化工具。

用 EXPLAIN ANALYZE 做量化性能对比

仓库中的 postgres/lower-is-faster-than-ilike.md 展示了EXPLAIN ANALYZE的另一典型用法——量化对比两种写法的真实耗时。该记录通过对比:

select * from users where email ilike 'some-email@example.com';

与

select * from users where lower(email) = lower('some-email@example.com');

发现lower()写法耗时约 12ms,ilike写法约 17ms;而为lower(email)建立函数索引create unique index users_unique_lower_email_idx on users (lower(email));之后,lower()写法骤降至约 0.08ms。这正是EXPLAIN ANALYZE的价值所在:它提供的实际执行时间让不同实现方案的优劣一目了然,也验证了索引是否被真正利用。

在应用层(Rails)调用 EXPLAIN

PostgreSQL 之外,应用框架也常把EXPLAIN暴露给开发者。仓库 rails/perform-sql-explain-with-activerecord.md 记录了在 Pry 会话中通过 ActiveRecord 对任意ActiveRecord::Relation直接调用#explain:

Recipe.all.joins(:ingredient_amounts).explain Recipe Load (0.9ms) SELECT "recipes".* FROM "recipes" INNER JOIN "ingredient_amounts" ON "ingredient_amounts"."recipe_id" = "recipes"."id" => EXPLAIN for: SELECT "recipes".* FROM "recipes" INNER JOIN "ingredient_amounts" ON "ingredient_amounts"."recipe_id" = "recipes"."id" QUERY PLAN ---------------------------------------------------------------------------- Hash Join (cost=1.09..26.43 rows=22 width=148) Hash Cond: (ingredient_amounts.recipe_id = recipes.id) -> Seq Scan on ingredient_amounts (cost=0.00..21.00 rows=1100 width=4) -> Hash (cost=1.04..1.04 rows=4 width=148) -> Seq Scan on recipes (cost=0.00..1.04 rows=4 width=148) (5 rows)

注意 ActiveRecord 的#explain只产生成本估算(等价于EXPLAIN),不会执行查询,这与本文强调的核心区别一致——在 ORM 层面默认就选择了无副作用的版本。

配合 psql 工具:\timing与statement_timeout

探查性能时还可以借助 psql 与 PostgreSQL 自身的配套设施:

  • 仓库 postgres/turn-timing-on.md 记录在psql中执行\timing可以开关每条查询的耗时显示(毫秒级),适合在平时随手观察查询速度;
  • 仓库 postgres/prevent-a-query-from-running-too-long.md 记录通过set statement_timeout to '500';(或带单位如'15s')为当前连接设置语句超时,避免调优过程中一条失控的EXPLAIN ANALYZE长时间占用资源。

小结

EXPLAIN与EXPLAIN ANALYZE唯一的本质区别就是"是否真实执行查询":前者只输出规划器的成本估算,后者会真实执行并附上实际耗时、行数等实测数据。对 SELECT 而言选哪个问题不大;对 INSERT、UPDATE、DELETE 等写语句,务必先想清楚——你只是想看执行计划(用EXPLAIN),还是愿意接受数据被真实改动(用EXPLAIN ANALYZE)。配合事务回滚、JSON/YAML/XML 格式输出、\timing与statement_timeout,你就能在安全的前提下获得最准确的执行画像。

  • 文档
  • 教程
  • 知识库

【免费下载链接】til

:memo: Today I Learned

项目地址:https://gitcode.com/gh_mirrors/ti/til
点击查看免费下载

相关推荐

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/8 6:43:40

资料明明存过却找不到,Google给了新办法

摘要:EmbeddingGemma 2把文字、图片、录音和视频放进同一套检索空间,让人有机会凭内容找资料。它可以在本地运行,但要成为好用的知识库,还需要切片、索引和结果定位。理解这些环节,才能判断这次更新对自己有什么用。 电…

作者头像 李华
网站建设 2026/10/8 6:42:45

告别tail -f的三大痛点:ponytail轻量日志追读工具原理与实战

如果你维护过线上服务,多半遇到过这种局面:某个服务突然报错,你想看日志最新写入的几行,然而文件已经几百 MB,tail -f刷起来全是无关噪音,你想要的“最新一行”早被淹没在心跳、探活、健康检查的刷屏里。更…

作者头像 李华
网站建设 2026/10/8 6:41:42

基于eFuse和MCU的嵌入式电源路径保护方案:TPS259483与K60实战

做嵌入式系统这几年,我见过太多“莫名其妙就挂了”的板子:客户那边一次接线失误、一次热插拔、一次电源纹波抖动,返回来的设备就是不开机。拆开查,MCU 本身没坏,坏的是电源路径上某个不该被忽略的环节。这篇文章要聊的…

作者头像 李华
网站建设 2026/10/8 6:41:14

Python网络舆情分析系统源码拆解:前后端+MySQL课设项目部署与二次开发

简介:这份基于Python的网络舆情分析系统以完整前后端与MySQL数据库呈现,面向舆情监控管理人员以及毕业设计、课程设计开发者。系统支持多用户并行使用,管理员可管理用户与言论数据,通过对各类网络平台言论的情感分析,以…

作者头像 李华