热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

PostgreSQL读懂执行计划(一)

这里写自定义目录标题前言执行计划常用命令参数解读常用组合执行计划解读关键字常见扫描方式SeqScanIndexOnlyScanIndexScanBitmapIndexScan+Bi

这里写自定义目录标题



  • 前言

  • 执行计划常用命令

  • 参数解读

  • 常用组合

  • 执行计划解读



    • 关键字

    • 常见扫描方式



      • Seq Scan

      • Index Only Scan

      • Index Scan

      • Bitmap Index Scan+Bitmap Heap Scan

      • Hash Join

      • Nested Loop

      • Merge Join





  • 小结

前言

PostgreSQL为每个收到查询产生一个查询计划。 选择正确的计划来匹配查询结构和数据的属性对于好的性能来说绝对是最关键的,因此系统包含了一个复杂的规划器来尝试选择好的计划。 你可以使用EXPLAIN命令察看规划器为任何查询生成的查询计划。


执行计划常用命令

EXPLAIN [ ( option [, ...] ) ] statement
EXPLAIN [ ANALYZE ] [ VERBOSE ] statement
这里 option可以是:
ANALYZE [ boolean ]
VERBOSE [ boolean ]
COSTS [ boolean ]
BUFFERS [ boolean ]
TIMING [ boolean ]
SUMMARY [ boolean ]
FORMAT { TEXT | XML | JSON | YAML }

参数解读


  • ANALYZE,执行命令并且显示实际的运行时间和其他统计信息。这个参数默认被设置为FALSE。

  • VERBOSE,显示关于计划的额外信息。特别是:计划树中每个结点的输出列列表、模式限定的表和函数名、总是把表达式中的变量标上它们的范围表别名,以及总是打印统计信息被显示的每个触发器的名称。这个参数默认被设置为FALSE。

  • COSTS,包括每一个计划结点的估计启动和总代价,以及估计的行数和每行的宽度。这个参数默认被设置为TRUE。

  • BUFFERS,包括缓冲区使用的信息。特别是:共享块命中、读取、标记为脏和写入的次数、本地块命中、读取、标记为脏和写入的次数、以及临时块读取和写入的次数。只有当ANALYZE也被启用时,这个参数才能使用。它的默认被设置为FALSE。

  • TIMING,在输出中包括实际启动时间以及在每个结点中花掉的时间。只有当ANALYZE也被启用时,这个参数才能使用。它的默认被设置为TRUE。

  • SUMMARY,在查询计划之后包含摘要信息(例如,总计的时间信息)。当使用ANALYZE 时默认包含摘要信息,但默认情况下不包含摘要信息,但可以使用此选项启用摘要信息。 使用EXPLAIN EXECUTE中的计划时间包括从缓存中获取计划所需的时间 以及重新计划所需的时间(如有必要)。

  • FORMAT,指定输出格式,可以是 TEXT、XML、JSON 或者 YAML。非文本输出包含和文本输出格式相同的信息,但是更容易被程序解析。这个参数默认被设置为TEXT。

  • statement,你想查看其执行计划的任何SELECT、INSERT、UPDATE、DELETE、VALUES、EXECUTE、DECLARE、CREATE TABLE AS或者CREATE MATERIALIZED VIEW AS语句。


常用组合

  • 一般查询

--在不需要真正执行sql时,需把analyze去掉
explain analyze select;


  • 查询缓存及详细信息

--在不需要真正执行sql时,需把analyze去掉
explain (analyze,verbose,buffers) select;

执行计划解读

关键字

首先我们看下执行计划常见的关键字

db_test=# explain (analyze,verbose,buffers) select * from db_test.t_test;
QUERY PLAN
-----------------------------------------------------------------------------------------------------------------
Seq Scan on db_test.t_test(cost=0.00..22.32 rows=1032 width=56) (actual time=0.060..1.167 rows=1032 loops=1)
Output: c_bh, n_dm, c_ah
Buffers: shared hit=12
Planning Time: 0.283 ms
Execution Time: 1.730 ms

关键字解读

cost=0.00..22.32,0.00代表启动成本,22.32代表返回所有数据的成本。
rows=1032:表示返回多少行。
width=56,表示每行平均宽度。
actual time=0.060..1.167,实际花费的时间。
loops=1,循环的次数
Output,输出的字段名
Buffers,缓冲命中数
Planning Time,生成执行计划的时间
Execution Time,执行执行计划的时间

常见扫描方式

PostgreSQL中数据扫描方式很多,常见有如下几种


Seq Scan

全表顺序扫描

db_test=# explain (analyze,verbose,buffers) select * from db_test.t_ms_aj;
QUERY PLAN
-----------------------------------------------------------------------------------------------------------------
Seq Scan on db_test.t_ms_aj (cost=0.00..22.32 rows=1032 width=56) (actual time=0.060..1.167 rows=1032 loops=1)
Output: c_bh, n_dm, c_ah
Buffers: shared hit=12
Planning Time: 0.283 ms
Execution Time: 1.730 ms

Index Only Scan

按索引顺序扫描,根据VM文件的BIT位判断是否需要回表扫描。

db_test=# explain (analyze,verbose,buffers) select c_bh from db_test.t_ms_aj where c_bh='db22f5a4f828d0f4eaa0b70679a4d637';
QUERY PLAN
------------------------------------------------------------------------------------------------------------------------------------
Index Only Scan using t_ms_aj_pkey on db_test.t_ms_aj (cost=0.28..8.29 rows=1 width=33) (actual time=0.079..0.081 rows=1 loops=1)
Output: c_bh
Index Cond: (t_ms_aj.c_bh = 'db22f5a4f828d0f4eaa0b70679a4d637'::bpchar)
Heap Fetches: 1
Buffers: shared hit=3
Planning Time: 0.139 ms
Execution Time: 0.166 ms

Index Scan

按索引顺序扫描,并回表。

db_test=# explain (analyze,buffers) select * from db_test.t_ms_aj where c_bh='db22f5a4f828d0f4eaa0b70679a4d637';
QUERY PLAN
-----------------------------------------------------------------------------------------------------------------------
Index Scan using t_ms_aj_pkey on t_ms_aj (cost=0.28..8.29 rows=1 width=56) (actual time=0.890..0.894 rows=1 loops=1)
Index Cond: (c_bh = 'db22f5a4f828d0f4eaa0b70679a4d637'::bpchar)
Buffers: shared hit=3
Planning Time: 0.376 ms
Execution Time: 1.136 ms

Bitmap Index Scan+Bitmap Heap Scan

按索引取得的BLOCKID排序,然后根据BLOCKID顺序回表扫描,然后再根据条件过滤掉不符合条件的记录。
这种扫描方法,主要解决了离散数据(索引字段的逻辑顺序与记录的实际存储顺序非常离散的情况),需要大量离散回表扫描的情况。

db_test=# explain (analyze,verbose,buffers) select * from db_test.t_ms_aj_bak where n_dm=12;
QUERY PLAN
-----------------------------------------------------------------------------------------------------------------------------------
Bitmap Heap Scan on db_test.t_ms_aj_bak (cost=100.85..1303.26 rows=5233 width=56) (actual time=1.477..107.896 rows=5204 loops=1)
Output: c_bh, n_dm, c_ah
Recheck Cond: (t_ms_aj_bak.n_dm = 12)
Heap Blocks: exact=1125
Buffers: shared hit=1126 read=15
-> Bitmap Index Scan on i_ms_aj_bak_n_dm (cost=0.00..99.54 rows=5233 width=0) (actual time=1.260..1.260 rows=5204 loops=1)
Index Cond: (t_ms_aj_bak.n_dm = 12)
Buffers: shared hit=1 read=15
Planning Time: 0.114 ms
Execution Time: 109.361 ms

Hash Join

哈希JOIN

db_test=# explain (analyze,verbose,buffers) select aj.c_bh from db_test.t_ms_aj aj join db_test.t_ms_dsr dsr on dsr.c_bh_aj=aj.c_bh;
QUERY PLAN
------------------------------------------------------------------------------------------------------------------------------------
Hash Join (cost=35.22..5378.59 rows=2307 width=33) (actual time=3.121..1254.234 rows=2074 loops=1)
Output: aj.c_bh
Inner Unique: true
Hash Cond: (dsr.c_bh_aj = aj.c_bh)
Buffers: shared hit=2828
-> Seq Scan on db_test.t_ms_dsr dsr (cost=0.00..4817.86 rows=200186 width=33) (actual time=0.013..598.660 rows=200186 loops=1)
Output: dsr.c_bh, dsr.c_bh_aj, dsr.c_name
Buffers: shared hit=2816
-> Hash (cost=22.32..22.32 rows=1032 width=33) (actual time=3.089..3.089 rows=1032 loops=1)
Output: aj.c_bh
Buckets: 2048 Batches: 1 Memory Usage: 82kB
Buffers: shared hit=12
-> Seq Scan on db_test.t_ms_aj aj (cost=0.00..22.32 rows=1032 width=33) (actual time=0.010..1.860 rows=1032 loops=1)
Output: aj.c_bh
Buffers: shared hit=12
Planning Time: 0.396 ms
Execution Time: 1257.348 ms

Nested Loop

嵌套循环。其中一个表扫描一次,另一个表则循环多次。

db_test=# explain analyze select aj.c_bh from db_test.t_ms_aj aj join db_test.t_ms_dsr dsr on dsr.c_bh_aj=aj.c_bh where aj.n_dm=20;
QUERY PLAN
-----------------------------------------------------------------------------------------------------------------------------
Nested Loop (cost=8.87..263.50 rows=45 width=33) (actual time=0.058..0.405 rows=37 loops=1)
-> Bitmap Heap Scan on t_ms_aj aj (cost=4.43..17.09 rows=20 width=33) (actual time=0.028..0.067 rows=20 loops=1)
Recheck Cond: (n_dm = 20)
Heap Blocks: exact=10
-> Bitmap Index Scan on i_ms_aj_n_dm (cost=0.00..4.43 rows=20 width=0) (actual time=0.018..0.019 rows=20 loops=1)
Index Cond: (n_dm = 20)
-> Bitmap Heap Scan on t_ms_dsr dsr (cost=4.44..12.30 rows=2 width=33) (actual time=0.014..0.015 rows=2 loops=20)
Recheck Cond: (c_bh_aj = aj.c_bh)
Heap Blocks: exact=20
-> Bitmap Index Scan on i_ms_dsr_c_bh (cost=0.00..4.43 rows=2 width=0) (actual time=0.011..0.011 rows=2 loops=20)
Index Cond: (c_bh_aj = aj.c_bh)
Planning Time: 0.409 ms
Execution Time: 0.555 ms

Merge Join

Merge Join,需要两个JOIN的表的KEY都是先排好顺序的,如果有索引没有排序过程。Merge Join两个表都只扫描一次。

db_test=# explain analyze select aj.c_bh from db_test.t_ms_aj aj join db_test.t_ms_dsr dsr on dsr.c_bh=aj.c_ah;
QUERY PLAN
-------------------------------------------------------------------------------------------------------------------------------------------------
Gather (cost=14985.80..15689.01 rows=1032 width=33) (actual time=944.856..951.963 rows=0 loops=1)
Workers Planned: 1
Workers Launched: 1
-> Merge Join (cost=13985.80..14585.81 rows=607 width=33) (actual time=851.573..851.573 rows=0 loops=2)
Merge Cond: (dsr.c_bh = (aj.c_ah)::bpchar)
-> Sort (cost=13911.82..14206.21 rows=117756 width=33) (actual time=747.508..792.472 rows=100093 loops=2)
Sort Key: dsr.c_bh
Sort Method: quicksort Memory: 11282kB
Worker 0: Sort Method: quicksort Memory: 10503kB
-> Parallel Seq Scan on t_ms_dsr dsr (cost=0.00..3993.56 rows=117756 width=33) (actual time=0.035..115.401 rows=100093 loops=2)
-> Sort (cost=73.98..76.56 rows=1032 width=52) (actual time=2.963..3.154 rows=338 loops=2)
Sort Key: aj.c_ah USING <
Sort Method: quicksort Memory: 194kB
Worker 0: Sort Method: quicksort Memory: 194kB
-> Seq Scan on t_ms_aj aj (cost&#061;0.00..22.32 rows&#061;1032 width&#061;52) (actual time&#061;0.082..0.545 rows&#061;1032 loops&#061;2)
Planning Time: 0.481 ms
Execution Time: 952.152 ms

小结

本文地址:https://blog.csdn.net/qq_37531186/article/details/108995575



推荐阅读
  • 在使用 Cacti 进行监控时,发现已运行的转码机未产生流量,导致 Cacti 监控界面显示该转码机处于宕机状态。进一步检查 Cacti 日志,发现数据库中存在 SQL 查询失败的问题,错误代码为 145。此问题可能是由于数据库表损坏或索引失效所致,建议对相关表进行修复操作以恢复监控功能。 ... [详细]
  • MySQL初级篇——字符串、日期时间、流程控制函数的相关应用
    文章目录:1.字符串函数2.日期时间函数2.1获取日期时间2.2日期与时间戳的转换2.3获取年月日、时分秒、星期数、天数等函数2.4时间和秒钟的转换2. ... [详细]
  • 2022年2月 微信小程序 app.json 配置详解:启用调试模式
    本文将详细介绍如何在微信小程序的 app.json 文件中启用调试模式(debug),并通过实际案例展示其配置方法和应用场景。 ... [详细]
  • com.sun.javadoc.PackageDoc.exceptions()方法的使用及代码示例 ... [详细]
  • 本文详细介绍了MySQL数据库的基础语法与核心操作,涵盖从基础概念到具体应用的多个方面。首先,文章从基础知识入手,逐步深入到创建和修改数据表的操作。接着,详细讲解了如何进行数据的插入、更新与删除。在查询部分,不仅介绍了DISTINCT和LIMIT的使用方法,还探讨了排序、过滤和通配符的应用。此外,文章还涵盖了计算字段以及多种函数的使用,包括文本处理、日期和时间处理及数值处理等。通过这些内容,读者可以全面掌握MySQL数据库的核心操作技巧。 ... [详细]
  • 深入解析 SQL 数据库查询技术
    本文深入探讨了SQL数据库查询技术,重点讲解了单表查询的各种方法。首先,介绍了如何从表中选择特定的列,包括查询指定列、查询所有列以及计算值的查询。此外,还详细解释了如何使用列别名来修改查询结果的列标题,并介绍了更名运算的应用场景和实现方式。通过这些内容,读者可以更好地理解和掌握SQL查询的基本技巧和高级用法。 ... [详细]
  • Python 程序转换为 EXE 文件:详细解析 .py 脚本打包成独立可执行文件的方法与技巧
    在开发了几个简单的爬虫 Python 程序后,我决定将其封装成独立的可执行文件以便于分发和使用。为了实现这一目标,首先需要解决的是如何将 Python 脚本转换为 EXE 文件。在这个过程中,我选择了 Qt 作为 GUI 框架,因为之前对此并不熟悉,希望通过这个项目进一步学习和掌握 Qt 的基本用法。本文将详细介绍从 .py 脚本到 EXE 文件的整个过程,包括所需工具、具体步骤以及常见问题的解决方案。 ... [详细]
  • MySQL索引详解及其优化策略
    本文详细解析了MySQL索引的概念、数据结构及管理方法,并探讨了如何正确使用索引以提升查询性能。文章还深入讲解了联合索引与覆盖索引的应用场景,以及它们在优化数据库性能中的重要作用。此外,通过实例分析,进一步阐述了索引在高读写比系统中的必要性和优势。 ... [详细]
  • 机器学习算法:SVM(支持向量机)
    SVM算法(SupportVectorMachine,支持向量机)的核心思想有2点:1、如果数据线性可分,那么基于最大间隔的方式来确定超平面,以确保全局最优, ... [详细]
  • 本文节选自《NLTK基础教程——用NLTK和Python库构建机器学习应用》一书的第1章第1.2节,作者Nitin Hardeniya。本文将带领读者快速了解Python的基础知识,为后续的机器学习应用打下坚实的基础。 ... [详细]
  • 使用jqTransform插件美化表单
    jqTransform 是由 DFC Engineering 开发的一款 jQuery 插件,专用于美化表单元素,操作简便,能够美化包括输入框、单选按钮、多行文本域、下拉选择框和复选框在内的所有表单元素。 ... [详细]
  • 在 Kubernetes 中,Pod 的调度通常由集群的自动调度策略决定,这些策略主要关注资源充足性和负载均衡。然而,在某些场景下,用户可能需要更精细地控制 Pod 的调度行为,例如将特定的服务(如 GitLab)部署到特定节点上,以提高性能或满足特定需求。本文深入解析了 Kubernetes 的亲和性调度机制,并探讨了多种优化策略,帮助用户实现更高效、更灵活的资源管理。 ... [详细]
  • PHP 数组逆序排列方法及常用排序函数详解 ... [详细]
  • 在Kubernetes上部署多个Mitmproxy代理服务器以实现高效流量管理 ... [详细]
  • 在处理UVA11987问题时,关键在于实现并查集结构以支持删除操作。特别地,当需要删除某个节点时,如果该节点不是根节点,则处理相对简单;然而,若删除的是根节点,则需要进行额外的处理来维护集合的连通性。本文将详细介绍如何通过优化并查集算法,确保在删除根节点时仍能高效地维护数据结构的完整性和查询效率。 ... [详细]
author-avatar
小心大巧
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有