PostgreSql的Explain命令详解
- EXPLAIN SELECT * FROM tenk1;
 - QUERY PLAN
 - -------------------------------------------------------------
 - Seq Scan on tenk1 (cost=0.00..458.00 rows=10000 width=244)
 
因为这个查询没有WHERE子句,所以必须扫描表中的所有行,所以规划器选择使用简单的顺序扫描规划。括号中的数字从左到右依次是:
- 评估开始消耗。这是可以开始输出前的时间,比如排序节点的排序的时间。
 - 评估总消耗。假设查询从执行到结束的时间。有时父节点可能停止这个过程,比如LIMIT子句。
 - 评估查询节点的输出行数,假设该节点执行结束。
 - 评估查询节点的输出行的平均字节数。
 
- EXPLAIN SELECT * FROM tenk1 WHERE unique1 < 7000;
 - QUERY PLAN
 - ------------------------------------------------------------
 - Seq Scan on tenk1 (cost=0.00..483.00 rows=7001 width=244)
 - Filter: (unique1 < 7000)
 
- EXPLAIN SELECT * FROM tenk1 WHERE unique1 < 100;
 - QUERY PLAN
 - ------------------------------------------------------------------------------
 - Bitmap Heap Scan on tenk1 (cost=5.07..229.20 rows=101 width=244)
 - Recheck Cond: (unique1 < 100)
 - -> Bitmap Index Scan on tenk1_unique1 (cost=0.00..5.04 rows=101 width=0)
 - Index Cond: (unique1 < 100)
 
查询规划器决定使用两步规划:首先子查询节点查看索引找到符合条件的记录索引,然后外层查询节点将这些记录从表中提取出来。分别提取数据的成本要高于顺序读取,但因为不需要读取所有磁盘页,所以总消耗比较小。(其中Bitmap是系统排序的一种机制)
- EXPLAIN SELECT * FROM tenk1 WHERE unique1 < 100 AND stringu1 = 'xxx';
 - QUERY PLAN
 - ------------------------------------------------------------------------------
 - Bitmap Heap Scan on tenk1 (cost=5.04..229.43 rows=1 width=244)
 - Recheck Cond: (unique1 < 100)
 - Filter: (stringu1 = 'xxx'::name)
 - -> Bitmap Index Scan on tenk1_unique1 (cost=0.00..5.04 rows=101 width=0)
 - Index Cond: (unique1 < 100)
 
增加的条件stringu1='xxx'减少了输出记录数的评估,但没有减少时间消耗,应为系统还是要查询相同数量的记录。请注意stringu1不是索引条件。
- EXPLAIN SELECT * FROM tenk1 WHERE unique1 < 100 AND unique2 > 9000;
 - QUERY PLAN
 - -------------------------------------------------------------------------------------
 - Bitmap Heap Scan on tenk1 (cost=25.08..60.21 rows=10 width=244)
 - Recheck Cond: ((unique1 < 100) AND (unique2 > 9000))
 - -> BitmapAnd (cost=25.08..25.08 rows=10 width=0)
 - -> Bitmap Index Scan on tenk1_unique1 (cost=0.00..5.04 rows=101 width=0)
 - Index Cond: (unique1 < 100)
 - -> Bitmap Index Scan on tenk1_unique2 (cost=0.00..19.78 rows=999 width=0)
 - Index Cond: (unique2 > 9000)
 
这个查询条件的两个字段都有索引,索引不需要filre。
- EXPLAIN SELECT * FROM tenk1 WHERE unique1 < 100 AND unique2 > 9000 LIMIT 2;
 - QUERY PLAN
 - -------------------------------------------------------------------------------------
 - Limit (cost=0.29..14.48 rows=2 width=244)
 - -> Index Scan using tenk1_unique2 on tenk1 (cost=0.29..71.27 rows=10 width=244)
 - Index Cond: (unique2 > 9000)
 - Filter: (unique1 < 100)
 
这条查询的where条件和上面的一样,只是增加了LIMIT,所以不是所有数据都需要返回,规划器改变了规划。在索引扫描节点总消耗和返回记录数是运行玩查询之后的数值,但Limit节点预期时间消耗是15,所以总时间消耗是15.增加LIMIT会使启动时间小幅增加(0.25->0.29)。
- EXPLAIN SELECT *
 - FROM tenk1 t1, tenk2 t2
 - WHERE t1.unique1 < 10 AND t1.unique2 = t2.unique2;
 - QUERY PLAN
 - --------------------------------------------------------------------------------------
 - Nested Loop (cost=4.65..118.62 rows=10 width=488)
 - -> Bitmap Heap Scan on tenk1 t1 (cost=4.36..39.47 rows=10 width=244)
 - Recheck Cond: (unique1 < 10)
 - -> Bitmap Index Scan on tenk1_unique1 (cost=0.00..4.36 rows=10 width=0)
 - Index Cond: (unique1 < 10)
 - -> Index Scan using tenk2_unique2 on tenk2 t2 (cost=0.29..7.91 rows=1 width=244)
 - Index Cond: (unique2 = t1.unique2)
 
这个规划中有一个内连接的节点,它有两个子节点。节点摘要行的缩进反映了规划树的结构。最外层是一个连接节点,子节点是一个Bitmap扫描。外部节点位图扫描的消耗和记录数如同我们使用SELECT...WHERE unique1 < 10,因为这时t1.unique2 = t2.unique2还不相关。接下来为每一个从外部节点得到的记录运行内部查询节点。这里外部节点得到的数据的t1.unique2值是可用的,所以我们得到的计划和SELECT...WHEREt2.unique2=constant的情况类似。(考虑到缓存的因素评估的消耗可能要小一些)
- EXPLAIN SELECT *
 - FROM tenk1 t1, tenk2 t2
 - WHERE t1.unique1 < 10 AND t2.unique2 < 10 AND t1.hundred < t2.hundred;
 - QUERY PLAN
 - ---------------------------------------------------------------------------------------------
 - Nested Loop (cost=4.65..49.46 rows=33 width=488)
 - Join Filter: (t1.hundred < t2.hundred)
 - -> Bitmap Heap Scan on tenk1 t1 (cost=4.36..39.47 rows=10 width=244)
 - Recheck Cond: (unique1 < 10)
 - -> Bitmap Index Scan on tenk1_unique1 (cost=0.00..4.36 rows=10 width=0)
 - Index Cond: (unique1 < 10)
 - -> Materialize (cost=0.29..8.51 rows=10 width=244)
 - -> Index Scan using tenk2_unique2 on tenk2 t2 (cost=0.29..8.46 rows=10 width=244)
 - Index Cond: (unique2 < 10)
 
条件t1.hundred<t2.hundred不在tenk2_unique2索引中,所以这个条件出现在连接节点中。这将减少连接节点的评估输出记录数,但不会改变子节点的扫描数。
- EXPLAIN SELECT *
 - FROM tenk1 t1, tenk2 t2
 - WHERE t1.unique1 < 100 AND t1.unique2 = t2.unique2;
 - QUERY PLAN
 - ------------------------------------------------------------------------------------------
 - Hash Join (cost=230.47..713.98 rows=101 width=488)
 - Hash Cond: (t2.unique2 = t1.unique2)
 - -> Seq Scan on tenk2 t2 (cost=0.00..445.00 rows=10000 width=244)
 - -> Hash (cost=229.20..229.20 rows=101 width=244)
 - -> Bitmap Heap Scan on tenk1 t1 (cost=5.07..229.20 rows=101 width=244)
 - Recheck Cond: (unique1 < 100)
 - -> Bitmap Index Scan on tenk1_unique1 (cost=0.00..5.04 rows=101 width=0)
 - Index Cond: (unique1 < 100)
 
这里规划器选择使用hash join,将一个表的数据存入内存中的哈希表,然后扫描另一个表并和哈希表中的每一条数据进行匹配。
- EXPLAIN SELECT *
 - FROM tenk1 t1, onek t2
 - WHERE t1.unique1 < 100 AND t1.unique2 = t2.unique2;
 - QUERY PLAN
 - ------------------------------------------------------------------------------------------
 - Merge Join (cost=198.11..268.19 rows=10 width=488)
 - Merge Cond: (t1.unique2 = t2.unique2)
 - -> Index Scan using tenk1_unique2 on tenk1 t1 (cost=0.29..656.28 rows=101 width=244)
 - Filter: (unique1 < 100)
 - -> Sort (cost=197.83..200.33 rows=1000 width=244)
 - Sort Key: t2.unique2
 - -> Seq Scan on onek t2 (cost=0.00..148.00 rows=1000 width=244)
 
Merge Join需要已经排序的输入数据。在这个规划中按正确顺序索引扫描tenk1的数据,但是对onek表执行排序和顺序扫描,因为需要在这个表中查询多条数据。因为索引扫描需要访问不连续的磁盘,所以索引扫描多条数据时会频繁使用排序顺序扫描(Sequential-scan-and-sort)。
- SET enable_sort = off;
 - EXPLAIN SELECT *
 - FROM tenk1 t1, onek t2
 - WHERE t1.unique1 < 100 AND t1.unique2 = t2.unique2;
 - QUERY PLAN
 - ------------------------------------------------------------------------------------------
 - Merge Join (cost=0.56..292.65 rows=10 width=488)
 - Merge Cond: (t1.unique2 = t2.unique2)
 - -> Index Scan using tenk1_unique2 on tenk1 t1 (cost=0.29..656.28 rows=101 width=244)
 - Filter: (unique1 < 100)
 - -> Index Scan using onek_unique2 on onek t2 (cost=0.28..224.79 rows=1000 width=244)
 
显示测结果表明,规划器认为索引扫描比排序顺序扫描消耗高12%。当然下一个问题就是规划器的评估为什么是正确的。我们可以通过EXPLAIN ANALYZE进行考察。
- EXPLAIN ANALYZE SELECT *
 - FROM tenk1 t1, tenk2 t2
 - WHERE t1.unique1 < 10 AND t1.unique2 = t2.unique2;
 - QUERY PLAN
 - ---------------------------------------------------------------------------------------------------------------------------------
 - Nested Loop (cost=4.65..118.62 rows=10 width=488) (actual time=0.128..0.377 rows=10 loops=1)
 - -> Bitmap Heap Scan on tenk1 t1 (cost=4.36..39.47 rows=10 width=244) (actual time=0.057..0.121 rows=10 loops=1)
 - Recheck Cond: (unique1 < 10)
 - -> Bitmap Index Scan on tenk1_unique1 (cost=0.00..4.36 rows=10 width=0) (actual time=0.024..0.024 rows=10 loops=1)
 - Index Cond: (unique1 < 10)
 - -> Index Scan using tenk2_unique2 on tenk2 t2 (cost=0.29..7.91 rows=1 width=244) (actual time=0.021..0.022 rows=1 loops=10)
 - Index Cond: (unique2 = t1.unique2)
 - Total runtime: 0.501 ms
 
注意,实际时间(actual time)的值是已毫秒为单位的实际时间,cost是评估的消耗,是个虚拟单位时间,所以他们看起来不匹配。
- EXPLAIN ANALYZE SELECT *
 - FROM tenk1 t1, tenk2 t2
 - WHERE t1.unique1 < 100 AND t1.unique2 = t2.unique2 ORDER BY t1.fivethous;
 - QUERY PLAN
 - --------------------------------------------------------------------------------------------------------------------------------------------
 - Sort (cost=717.34..717.59 rows=101 width=488) (actual time=7.761..7.774 rows=100 loops=1)
 - Sort Key: t1.fivethous
 - Sort Method: quicksort Memory: 77kB
 - -> Hash Join (cost=230.47..713.98 rows=101 width=488) (actual time=0.711..7.427 rows=100 loops=1)
 - Hash Cond: (t2.unique2 = t1.unique2)
 - -> Seq Scan on tenk2 t2 (cost=0.00..445.00 rows=10000 width=244) (actual time=0.007..2.583 rows=10000 loops=1)
 - -> Hash (cost=229.20..229.20 rows=101 width=244) (actual time=0.659..0.659 rows=100 loops=1)
 - Buckets: 1024 Batches: 1 Memory Usage: 28kB
 - -> Bitmap Heap Scan on tenk1 t1 (cost=5.07..229.20 rows=101 width=244) (actual time=0.080..0.526 rows=100 loops=1)
 - Recheck Cond: (unique1 < 100)
 - -> Bitmap Index Scan on tenk1_unique1 (cost=0.00..5.04 rows=101 width=0) (actual time=0.049..0.049 rows=100 loops=1)
 - Index Cond: (unique1 < 100)
 - Total runtime: 8.008 ms
 
排序节点(Sort)显示排序类型(一般是在内存还是在磁盘)和使用多少内存。哈希节点(Hash)显示哈希桶和批数以及使用内存的峰值。
- EXPLAIN ANALYZE SELECT * FROM tenk1 WHERE ten < 7;
 - QUERY PLAN
 - ---------------------------------------------------------------------------------------------------------
 - Seq Scan on tenk1 (cost=0.00..483.00 rows=7000 width=244) (actual time=0.016..5.107 rows=7000 loops=1)
 - Filter: (ten < 7)
 - Rows Removed by Filter: 3000
 - Total runtime: 5.905 ms
 
这个值在join节点上尤其有价值。"Rows Removed"只有在过滤条件过滤掉数据时才显示。
- EXPLAIN ANALYZE SELECT * FROM polygon_tbl WHERE f1 @> polygon '(0.5,2.0)';
 - QUERY PLAN
 - ------------------------------------------------------------------------------------------------------
 - Seq Scan on polygon_tbl (cost=0.00..1.05 rows=1 width=32) (actual time=0.044..0.044 rows=0 loops=1)
 - Filter: (f1 @> '((0.5,2))'::polygon)
 - Rows Removed by Filter: 4
 - Total runtime: 0.083 ms
 
规划器认为(正确的)这样的表太小以至于不需要索引扫描,所以采用顺序扫描所有行经行条件检查。
- SET enable_seqscan TO off;
 - EXPLAIN ANALYZE SELECT * FROM polygon_tbl WHERE f1 @> polygon '(0.5,2.0)';
 - QUERY PLAN
 - --------------------------------------------------------------------------------------------------------------------------
 - Index Scan using gpolygonind on polygon_tbl (cost=0.13..8.15 rows=1 width=32) (actual time=0.062..0.062 rows=0 loops=1)
 - Index Cond: (f1 @> '((0.5,2))'::polygon)
 - Rows Removed by Index Recheck: 1
 - Total runtime: 0.144 ms
 
这里我们可以看到索引返回一条候选数据,但被过滤条件拒绝。这是因为GiST索引在多边形包含检测上是松散的"lossy":它实际返回哪些和多边形交叠的数据,然后我们还需要针对这些数据做包含检测。
- EXPLAIN (ANALYZE, BUFFERS) SELECT * FROM tenk1 WHERE unique1 < 100 AND unique2 > 9000;
 - QUERY PLAN
 - ---------------------------------------------------------------------------------------------------------------------------------
 - Bitmap Heap Scan on tenk1 (cost=25.08..60.21 rows=10 width=244) (actual time=0.323..0.342 rows=10 loops=1)
 - Recheck Cond: ((unique1 < 100) AND (unique2 > 9000))
 - Buffers: shared hit=15
 - -> BitmapAnd (cost=25.08..25.08 rows=10 width=0) (actual time=0.309..0.309 rows=0 loops=1)
 - Buffers: shared hit=7
 - -> Bitmap Index Scan on tenk1_unique1 (cost=0.00..5.04 rows=101 width=0) (actual time=0.043..0.043 rows=100 loops=1)
 - Index Cond: (unique1 < 100)
 - Buffers: shared hit=2
 - -> Bitmap Index Scan on tenk1_unique2 (cost=0.00..19.78 rows=999 width=0) (actual time=0.227..0.227 rows=999 loops=1)
 - Index Cond: (unique2 > 9000)
 - Buffers: shared hit=5
 - Total runtime: 0.423 ms
 
- BEGIN;
 - EXPLAIN ANALYZE UPDATE tenk1 SET hundred = hundred + 1 WHERE unique1 < 100;
 - QUERY PLAN
 - --------------------------------------------------------------------------------------------------------------------------------
 - Update on tenk1 (cost=5.07..229.46 rows=101 width=250) (actual time=14.628..14.628 rows=0 loops=1)
 - -> Bitmap Heap Scan on tenk1 (cost=5.07..229.46 rows=101 width=250) (actual time=0.101..0.439 rows=100 loops=1)
 - Recheck Cond: (unique1 < 100)
 - -> Bitmap Index Scan on tenk1_unique1 (cost=0.00..5.04 rows=101 width=0) (actual time=0.043..0.043 rows=100 loops=1)
 - Index Cond: (unique1 < 100)
 - Total runtime: 14.727 ms
 - ROLLBACK;
 
当查询是INSERT,UPDATE或DELETE命令时,在顶级节点是实施对表的变更。在这下面的节点实行定位旧数据计算新数据的工作。所以我们看到一样的bitmap索引扫描,并返回给Update节点。值得注意的是虽然修改数据的节点可能需要相当长的运行时间(在这里它消耗了大部分的时间),规划器却没有再评估时间中添加任何消耗,这是因为更新工作对于任何查询规划都是一样的,所以并不影响规划器的决策。
PostgreSql的Explain命令详解的更多相关文章
- 转载:MySQL EXPLAIN 命令详解学习
		
转载自:https://blog.csdn.net/mchdba/article/details/9190771 MySQL EXPLAIN 命令详解 MySQL的EXPLAIN命令用于SQL语句的查 ...
 - MySQL EXPLAIN 命令详解
		
MySQL EXPLAIN 命令详解 MySQL的EXPLAIN命令用于SQL语句的查询执行计划(QEP).这条命令的输出结果能够让我们了解MySQL 优化器是如何执行SQL 语句的.这条命令并没有提 ...
 - 一次浴火重生的MySQL优化(EXPLAIN命令详解)
		
一直对SQL优化的技能心存无限的向往,之前面试的时候有很多面试官都会来一句,你会优化吗?我说我不太会,这时可能很多人就会有点儿说法了,比如会说不要使用通配符*去检索表.给常常使用的列建立索引.还有创建 ...
 - SQL中EXPLAIN命令详解---(转)
		
MySQL Explain详解 在日常工作中,我们会有时会开慢查询去记录一些执行时间比较久的SQL语句,找出这些SQL语句并不意味着完事了,些时我们常常用到explain这个命令来查看一个这些SQ ...
 - 2.mysql explain命令详解
		
EXPLAIN详解 SQL编写和解析 编写过程 select-distinct-from-join-on-where-group by-having-order by-limit- 解析过程 from ...
 - EXPLAIN 命令详解
		
在工作中,我们用于捕捉性能问题最常用的就是打开慢查询,定位执行效率差的SQL,那么当我们定位到一个SQL以后还不算完事,我们还需要知道该SQL的执行计划,比如是全表扫描,还是索引扫描,这些都需要通过E ...
 - 【MySQL】EXPLAIN命令详解
		
具体参考: https://www.cnblogs.com/gomysql/p/3720123.html 原文如下: 在工作中,我们用于捕捉性能问题最常用的就是打开慢查询,定位执行效率差的SQL,那么 ...
 - MySQL中EXPLAIN命令详解
		
explain显示了mysql如何使用索引来处理select语句以及连接表.可以帮助选择更好的索引和写出更优化的查询语句. 使用方法,在select语句前加上explain就可以了: 如: expla ...
 - MySQL的EXPLAIN命令详解(转)
		
explain显示了mysql如何使用索引来处理select语句以及连接表.可以帮助选择更好的索引和写出更优化的查询语句. 使用方法,在select语句前加上explain就可以了: 如: expla ...
 
随机推荐
- uiautomator——简单的将自动化测试与截图对比相结合使用的小例子!
			
1.在使用uiautomator进行自动化测试过程当中,执行某些动作之后,要进行判断是否已执行的动作成功时,需要添加检查点来判断,因此添加判断点除了使用id.text.view等方式进行之外,存在一种 ...
 - mysql获取表中日期的年月日时分秒
			
SELECT year(callTheRollTime) from schedule_account 获取年 SELECT month(callTheRollTime) from schedule_a ...
 - Asp.Net Core对接钉钉群机器人
			
钉钉作为企业办公越来越常用的软件,对于企业内部自研系统提供接口支持,以此来打通多平台下的数据,本次先使用最简单的钉钉群机器人完成多种形式的消息推送,参考钉钉开发文档中自定义机器人环节,此次尝试所花的时 ...
 - C++11新特性之tie、tuple的应用
			
//tuplestd::tuple<int, int, int, int, QString> Thorface::getUserInfoToJudgeOpendoor(QString st ...
 - CI持续集成系列之(九)代码发布脚本模板书写
			
前言 前面我们介绍了Jenkins来发布项目通过nginx来展示流程,那里只是提供了一个简单的测试脚本,接下来呢介绍一下一个比较完善的发布脚本,该脚本可实现从gitlab服务器获取代码,打包,部署到W ...
 - 如何配置Open Live Writer程序以便更好的为博客服务
			
Open Live Writer的前身是Windows live Writer 即(WLW)是一个免费的桌面应用程序,您可以使用它轻松发布丰富的内容到您的网络日志.WLW最终版为Windows Liv ...
 - centos7下报错: import requests ImportError: No module named requests
			
在网上扒了一个python脚本,在centos7上执行的时候报错: import requestsImportError: No module named requests 原因是:requests是 ...
 - 新DevOps八荣八耻
			
昀哥 20181001以随时可扩容可缩容可重启可切换机房流量为荣,以不能迁移为耻. 以可配置为荣,以硬编码为耻. 以系统互备为荣,以系统单点为耻. 以交付时有监控报警为荣,以交付裸奔系统为耻. 以无状 ...
 - 阿里云服务器建站——centos7部署apache+mysql+php
			
自己也是忙活了半天,才完成了阿里云服务器的建站,这里就来分享一下. 首先如果是要自己搭建一个网站的话,除了服务器以外还要购买域名,并且要去备案,一般在哪买的域名都有备案的系统,备案的话一般要两到三个星 ...
 - dubbo源码研究(一)
			
1. dubbo源码研究(一) 1.1. dubbo启动加载过程 我们知道,现在流行注解方式,用spring管理服务,dubbo最常用的就是@Reference和@Service了,那么我首先找到这两 ...