14.1.1 EXPLAIN基础
执行计划的架构,是一个执行节点的树形架构。树最底层的节点为扫描节点:他们从表返回原始行。针对不同的表访问方式有不同的扫描节点:顺序扫描(sequential scans)、索引扫描(index scans)和位图索引扫描(bitmap index scans)。还有一个不是来自表的行,例如VALUES子句,FROM中的返回集合的函数等,它们有自己的扫描节点类型。如果查询还需要进行连接,聚合,排序等其他操作,那在扫描节点之上还会有其他节点。执行计划的第一行含执行计划总的执行成本(cost)。
简单展示下执行计划:
EXPLAIN SELECT * FROM tenk1;
QUERY PLAN
-------------------------------------------------------------
Seq Scan on tenk1 (cost=0.00..458.00 rows=10000 width=244)
因为查询中没有where条件,所以执行计划使用sequential scan。括号中,从左到右是:
预估启动成本。
预估总成本。
预估返回行。
预估返回行的平均宽度(单位是bytes)。
执行计划中成本通过设置的执行计划参数估量(参见第19.7.2节)。一般以磁盘页读取为一个单元计算成本;所以一般seq_page_cost默认设为1.0,而其他的成本据此设置。
顶层节点的成本包含其子节点的成本。还有一点,执行计划中的成本,不会考虑通过更改执行计划无法改变的成本,比如从服务端将行传输到客户端所需的成本。
rows的值是该节点获得的行数,而不是检索或处理的行数。获得的行数一般会小于检索或处理的行数。
返回上例,执行以下语句:
SELECT relpages, reltuples FROM pg_class WHERE relname = 'tenk1';
示例中返回358个磁盘页和10000行。那预估的成本这样计算:读磁盘页*seq_page_cost+行扫描*cpu_tuple_cost。默认seq_page_cost为1.0,cpu_tuple_cost为0.01。所以默认成本为:358*1+10000*0.01=458。
接下来,为该查询加个where条件:
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)
可以看到,where条件使用Fileter附加到Seq Scan计划节点。这意味着,计划节点会检索所有行,但仅返回符合条件的行。
接下来执行:
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)
此处计划器选择两步走:首先使用索引查找行地址;然后从表中获取这些行。虽然分别获取行比直接从表中顺序读行耗费成本高,但是因为无需读取表中所有行,故此处分别获取行较直接读取行成本低。
接下来使用更复杂的条件:
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)
添加的条件虽然使输出行变少了,但因为仍然要检索所有行,所以成本未降。
有些情况下,计划器可以直接从索引中获取结果:
EXPLAIN SELECT * FROM tenk1 WHERE unique1 = 42;
QUERY PLAN
-----------------------------------------------------------------------------
Index Scan using tenk1_unique1 on tenk1 (cost=0.29..8.30 rows=1 width=244)Index Cond: (unique1 = 42)
一般只选取一行;或按照索引排序方法进行ORDER BY时会看到此类执行计划。
计划器也可能添加一个显式的sort步骤:
EXPLAIN SELECT * FROM tenk1 ORDER BY unique1;
QUERY PLAN
-------------------------------------------------------------------
Sort (cost=1109.39..1134.39 rows=10000 width=244)Sort Key: unique1-> Seq Scan on tenk1 (cost=0.00..445.00 rows=10000 width=244)
如果计划的一部分保证排序键前缀的排序,那么计划器可能会改而使用incremental sort步骤:
EXPLAIN SELECT * FROM tenk1 ORDER BY four, ten LIMIT 100;
QUERY PLAN
------------------------------------------------------------------------------------------------------
Limit (cost=521.06..538.05 rows=100 width=244)-> Incremental Sort (cost=521.06..2220.95 rows=10000 width=244) --将结果集分为多排序分支Sort Key: four, tenPresorted Key: four-> Index Scan using index_tenk1_on_four on tenk1 (cost=0.29..1510.08 rows=10000 width=244)
如果在查询列上都有索引,那么计划器可能会选择使用AND或者OR来进行索引组合:
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)
以下为LIMIT影响的示例:
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)
接下来使用之前讨论的列,尝试对两个表进行连接:
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)
更复杂一些:
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)
如果改变筛选条件,可能获得不一样的执行计划:
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)
还有一种连接方式为merge 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)
可使用19.7.1中的启用或禁用标志,改变执行计划。例如,假设我们不知道上例中对表onek顺序扫描和排序是否是最佳执行计划,那么可以这样:
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)
本文发布于:2024-02-02 12:47:58,感谢您对本站的认可!
本文链接:https://www.4u4v.net/it/170684927643902.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
留言与评论(共有 0 条评论) |