查看postgres版本
-bash-4.2$ pg_config --version
PostgreSQL 10.15
-bash-4.2$
参考文档:
http://postgres.cn/docs/10/using-explain.html http://postgres.cn/docs/10/runtime-config-query.html#GUC-SEQ-PAGE-COST
-- 关于explain的一些基础,其他详细信息可以参考官方文档
查询计划的结构是一个计划结点的树。最底层的结点是扫描结点:它们从表中返回未经处理的行。 不同的表访问模式有不同的扫描结点类型:顺序扫描、索引扫描、位图索引扫描。 也还有不是表的行来源,例如VALUES子句和FROM中返回集合的函数,它们有自己的结点类型。如果查询需要连接、聚集、排序、或者在未经处理的行上的其它操作,那么就会在扫描结点之上有其它额外的结点来执行这些操作。 并且,做这些操作通常都有多种方法,因此在这些位置也有可能出现不同的结点类型。 EXPLAIN给计划树中每个结点都输出一行,显示基本的结点类型和计划器为该计划结点的执行所做的开销估计。 第一行(最上层的结点)是对该计划的总执行开销的估计;计划器试图最小化的就是这个数字。
这里是一个简单的例子,只是用来显示输出看起来是什么样的:
EXPLAIN SELECT * FROM tenk1;
QUERY PLAN
-------------------------------------------------------------
Seq Scan on tenk1 (cost=0.00..458.00 rows=10000 width=244)
由于这个查询没有WHERE子句,它必须扫描表中的所有行,因此计划器只能选择使用一个简单的顺序扫描计划。被包含在圆括号中的数字是(从左至右):
估计的启动开销。在输出阶段可以开始之前消耗的时间,例如在一个排序结点里执行排序的时间。
估计的总开销。这个估计值基于的假设是计划结点会被运行到完成,即所有可用的行都被检索。不过实际上一个结点的父结点可能很快停止读所有可用的行(见下面的LIMIT例子)。
这个计划结点输出行数的估计值。同样,也假定该结点能运行到完成。
预计这个计划结点输出的行平均宽度(以字节计算)。
开销是用规划器的开销参数(参见第 19.7.2 节)所决定的捏造单位来衡量的。传统上以取磁盘页面为单位来度量开销; 也就是seq_page_cost将被按照习惯设为1.0,其它开销参数将相对于它来设置。
-- 在pg中插入的大量的数据
insert into a select generate_series(1,29044000);
mydb=# insert into a select generate_series(1,29044000);
INSERT 0 29044000
Time: 45712.869 ms (00:45.713)
mydb=#
mydb=# select count(*) from a;
count
----------
29044000
(1 row)
Time: 5143.810 ms (00:05.144)
mydb=#
-- 对该表创建索引
mydb=# create index idx_x on a(x);
CREATE INDEX
Time: 21230.629 ms (00:21.231)
mydb=#
--此时,查看查询语句的执行计划,可以发现,优化器进行了索引扫描
select x from a where x = 1000;
explain select x from a where x = 1000;
mydb=# select x from a where x = 1000;
x
------
1000
(1 row)
Time: 26.750 ms
mydb=# explain select x from a where x = 1000;
QUERY PLAN
--------------------------------------------------------------------
Index Only Scan using idx_x on a (cost=0.44..8.46 rows=1 width=4)
Index Cond: (x = 1000)
(2 rows)
Time: 4.294 ms
mydb=#
-- 查看该表所占用的页面大小,数据行大小,此时的值为优化器评估的值,不是很准确 (类似oracle的dba_tables里面的rows)
mydb=# select relpages,reltuples from pg_class where relname = 'a' ;
relpages | reltuples
----------+------------
128514 | 2.9044e+07
0 | 0
(2 rows)
Time: 13.917 ms
mydb=#
mydb=# select relpages,reltuples from pg_class where relname = 'idx_x' ;
relpages | reltuples
----------+------------
79638 | 2.9044e+07
(1 row)
Time: 0.850 ms
mydb=#
--关于explain的用法,可以看帮助,
mydb=# \help explain
Command: EXPLAIN
Description: show the execution plan of a statement
Syntax:
EXPLAIN [ ( option [, ...] ) ] statement
EXPLAIN [ ANALYZE ] [ VERBOSE ] statement
where option can be one of:
ANALYZE [ boolean ]
VERBOSE [ boolean ]
COSTS [ boolean ]
BUFFERS [ boolean ]
TIMING [ boolean ]
SUMMARY [ boolean ]
FORMAT { TEXT | XML | JSON | YAML }
mydb=# explain(analyze,verbose,costs) select * from foo;
QUERY PLAN
-----------------------------------------------------------------------------------------------------
Seq Scan on public.foo (cost=0.00..1.03 rows=3 width=12) (actual time=0.016..0.018 rows=3 loops=1)
Output: fooid, foosubid, fooname
Planning time: 0.122 ms
Execution time: 0.049 ms
(4 rows)
Time: 11.314 ms
mydb=#
mydb=# explain(analyze,costs,buffers,timing,summary) select * from foo;
QUERY PLAN
----------------------------------------------------------------------------------------------
Seq Scan on foo (cost=0.00..1.03 rows=3 width=12) (actual time=0.013..0.015 rows=3 loops=1)
Buffers: shared hit=1
Planning time: 0.072 ms
Execution time: 0.039 ms
(4 rows)
Time: 0.745 ms
-- 注意,analyze 会实际运行所explain的语句。可以将其包含在一个事务中,比如 :
begin;
explain select * from a;
rollback;
-- 以下为根据官-方文档来测试
-- 查看执行计划
explain select * from a;
mydb=# explain select * from a;
QUERY PLAN
-------------------------------------------------------------
Seq Scan on a (cost=0.00..418955.64 rows=29044164 width=4)
(1 row)
Time: 1.085 ms
mydb=#
-- 查看该表的一些信息,类似于统计信息,类似oracle的dba_tables里面的rows)
SELECT relpages, reltuples FROM pg_class WHERE relname = 'a';
mydb=# SELECT relpages, reltuples FROM pg_class WHERE relname = 'a';
relpages | reltuples
----------+-------------
128514 | 2.90442e+07
0 | 0
(2 rows)
Time: 1.166 ms
mydb=#
通过上面的查询,发现a表有128514个页面,2.90442e+07行数据。
开销计算公式为: (页面读取数*seq_page_cost)+(扫描的行数*cpu_tuple_cost)
其中seq_page_cost默认为1,cpu_tuple_cost默认为0.01
所以计算的开销为: 128514*1 + 29044000*0.01 = 418954 -- 计算的结果 和执行计划的值一样
-- 以下参考自: http://postgres.cn/docs/10/runtime-config-query.html#GUC-SEQ-PAGE-COST
seq_page_cost (floating point)
设置规划器对一系列顺序磁盘页面获取中的一次的代价估计。默认值是 1.0。
random_page_cost (floating point)
设置规划器对一次非顺序获取磁盘页面的代价估计。默认值是 4.0。减少这个值(相对于seq_page_cost)将导致系统更倾向于索引扫描;提高它将让索引扫描看起来相对更昂贵。 -- 类似oracle的 optimizer_index_cost_adj
对磁盘存储的随机访问通常比顺序访问要贵不止四倍。但是,由于对磁盘的大部分随机访问(例如被索引的读取)都被假定在高速缓冲中进行,所以使用了一个较低的默认值(4.0)。默认值可以被想成把随机访问建模为比顺序访问慢 40 倍,而期望 90% 的随机读取会被缓存。
cpu_tuple_cost (floating point)
设置规划器对一次查询中处理每一行的代价估计。默认值是 0.01。
cpu_index_tuple_cost (floating point)
设置规划器对一次索引扫描中处理每一个索引项的代价估计。默认值是 0.005。
cpu_operator_cost (floating point)
设置规划器对于一次查询中处理每个操作符或函数的代价估计。默认值是 0.0025。
parallel_setup_cost (floating point)
设置规划器对启动并行工作者进程的代价估计。默认是 1000。
parallel_tuple_cost (floating point)
设置规划器对于从一个并行工作者进程传递一个元组给另一个进程的代价估计。 默认是 0.1。
min_parallel_table_scan_size (integer)
设置必须扫描的最小表格数据量,以便考虑并行扫描。对于并行顺序扫描, 扫描的表格数据量始终等于表格的大小,但使用索引时, 扫描的表格数据量通常会少一些。默认值是8兆字节(8MB)。
min_parallel_index_scan_size (integer)
设置必须扫描的索引数据的最小数量,以便考虑并行扫描。请注意, 并行索引扫描通常不会触及整个索引;这是计划者认为实际上将被相关扫描触及的页数。 默认值是512千字节(512kB)。
effective_cache_size (integer)
设置规划器对一个单一查询可用的有效磁盘缓冲区尺寸的假设。这个参数会被考虑在使用一个索引的代价估计中,更高的数值会使得索引扫描更可能被使用,更低的数值会使得顺序扫描更可能被使用。在设置这个参数时,你还应该考虑PostgreSQL的共享缓冲区以及将被用于PostgreSQL数据文件的内核磁盘缓冲区。另外,还要考虑预计在不同表上的并发查询数目,因为它们必须共享可用的空间。这个参数对PostgreSQL分配的共享内存尺寸没有影响,它也不会保留内核磁盘缓冲,它只用于估计的目的。系统也不会假设在查询之间数据会保留在磁盘缓冲中。默认值是 4吉字节(4GB)。
其他,有空再补充。
END