暂无图片
暂无图片
5
暂无图片
暂无图片
暂无图片

尝试使用pg_store_plans,可以对标v$sql_plan

前言

在Oracle数据库中,我们可以通过v$sql_plan查看执行计划,但是在PostgreSQL中,等同于这个视图的功能竟然没有。在网上搜索了一下,发现pg_store_plans可以实现这个功能。

安装

我们从github上下载软件包来进行编译安装。

wget -c https://github.com/ossc-db/pg_store_plans/archive/refs/tags/1.5.zip--解压后,进入解压后的文件夹,执行make & make install。unzip 1.5.zipmake USE_PGXS=1make USE_PGXS=1 install

编译安装完成后,需要在shared_preload_libraries中设置参数pg_store_plans

shared_preload_libraries='pg_store_plans'

最后再创建extension就可以用了

create extension pg_store_plans;

建完就会有一个新的视图pg_store_plans,并且已经开始存储执行计划的数据了。

test=# \d pg_store_plans                            View "public.pg_store_plans"         Column          |           Type           | Collation | Nullable | Default -------------------------+--------------------------+-----------+----------+--------- userid                  | oid                      |           |          |  dbid                    | oid                      |           |          |  queryid                 | bigint                   |           |          |  planid                  | bigint                   |           |          |  queryid_stat_statements | bigint                   |           |          |  plan                    | text                     |           |          |  calls                   | bigint                   |           |          |  total_time              | double precision         |           |          |  min_time                | double precision         |           |          |  max_time                | double precision         |           |          |  mean_time               | double precision         |           |          |  stddev_time             | double precision         |           |          |  rows                    | bigint                   |           |          |  shared_blks_hit         | bigint                   |           |          |  shared_blks_read        | bigint                   |           |          |  shared_blks_dirtied     | bigint                   |           |          |  shared_blks_written     | bigint                   |           |          |  local_blks_hit          | bigint                   |           |          |  local_blks_read         | bigint                   |           |          |  local_blks_dirtied      | bigint                   |           |          |  local_blks_written      | bigint                   |           |          |  temp_blks_read          | bigint                   |           |          |  temp_blks_written       | bigint                   |           |          |  blk_read_time           | double precision         |           |          |  blk_write_time          | double precision         |           |          |  first_call              | timestamp with time zone |           |          |  last_call               | timestamp with time zone |           |          | 

这个视图有部分信息和pg_stat_statements重叠了。

user_id
: 执行语句的用户id。

dbid
: 执行语句的数据库的id。

queryid
:内部哈希码,根据语句的查询字符串计算。

planid
:执行计划的planid,类似oracle中的plan_hash_value。

queryid_stat_statements
:语句的queryid,类似oracle中的sql_id,可以和pg_stat_statements进行关联查看详细的sql语句。

plan
: 执行计划的文本。格式由参数pg_store_plans.plan_format
指定。

calls
:执行次数。

total_time
:语句使用的计划花费的总时间,以毫秒为单位

min_time
:语句使用的计划花费的最短时间,以毫秒为单位

max_time
:语句使用的计划花费的最长时间,以毫秒为单位

mean_time
:语句使用的计划平均时间,以毫秒为单位

stddev_time
:语句使用的计划花费时间的总体标准偏差,以毫秒为单位

rows
:语句使用的计划查询和影响的总行数。

shared_blks_hit
:语句使用的计划在共享块缓存命中总数。

shared_blks_read
:语句使用的计划读取的共享块总数。

shared_blks_dirtied
:语句使用的计划读取脏共享块总数。

shared_blks_written
:语句使用的计划写入的共享块总数。

local_blks_hit
:语句使用的计划命中的本地块缓存总数

local_blks_read
:语句使用的计划读取的本地块总数

local_blks_dirtied
:语句使用的计划弄脏的本地块总数

local_blks_written
:语句使用的计划写入的本地块总数

temp_blks_read
:语句使用计划读取的临时块总数

temp_blks_written
:语句使用计划写入的临时块总数

blk_read_time
:语句使用计划读取块所花费的总时间,以毫秒为单位(需要启用track_io_timing,否则为0)

blk_write_time
: 语句使用计划写入块所花费的总时间,以毫秒为单位 (需要启用track_io_timing,否则为0)

first_call
:语句使用计划首次调用的时间

last_call
: 语句使用计划最后一次调用的时间

例如:我们想查询某SQL语句和它的执行计划,只需要将pg_stat_statements和pg_store_plans进行关联就可以了。

test=# select a.query,b.plan,b.calls from pg_stat_statements a, pg_store_plans b where a.queryid=b.queryid_stat_statements and b.queryid_stat_statements=-235294569018386844 and a.dbid=13591;-[ RECORD 1 ]------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------query | SELECT e.extname AS "Name", e.extversion AS "Version", n.nspname AS "Schema", c.description AS "Description"                                  | FROM pg_catalog.pg_extension e LEFT JOIN pg_catalog.pg_namespace n ON n.oid = e.extnamespace LEFT JOIN pg_catalog.pg_description c ON           | c.objoid = e.oid AND c.classoid = $1::pg_catalog.regclass ORDER BY 1plan  | Sort  (cost=10.46..10.47 rows=1 width=158)                                                                                                       |   Sort Key: e.extname                                                                                                                           |   ->  Nested Loop Left  (cost=0.28..10.45 rows=1 width=158)                                                                                     |         Join Filter: (n.oid = e.extnamespace)                                                                                                   |         ->  Nested Loop Left  (cost=0.28..9.32 rows=1 width=98)                                                                                 |               ->  Seq Scan on pg_extension e  (cost=0.00..1.01 rows=1 width=76)                                                                 |               ->  Index Scan using pg_description_o_c_o_index on pg_description c  (cost=0.28..8.30 rows=1 width=30)                           |                     Index Cond: ((objoid = e.oid) AND (classoid = '3079'::oid))                                                                 |         ->  Seq Scan on pg_namespace n  (cost=0.00..1.06 rows=6 width=68)calls | 1

提供的函数

除了提供视图方便查询之外,还提供了一系列的函数。

pg_store_plans_reset
:丢弃到目前为止由pg_store_plans
所有收集的统计信息。默认情况下,只有超级用户才能执行此功能。

pg_store_plans
:pg_store_plans视图的定义是基于pg_store_plans函数的。

pg_store_hash_query
:该函数计算查询文本的哈希值。计算pg_store_plans的查询id使用相同的算法,该函数可以与pg_store_plans结合使用。

test=# select public.pg_store_plans_hash_query('select 1');-[ RECORD 1 ]-------------+-----------pg_store_plans_hash_query | 2042559815

pg_store_plans_textplan
:当你把参数pg_store_plans.plan_formats设置为'raw'的时候会显示短格式json,短格式json是pg_store_plans中的计划的内部格式,这个函数可以将短格式的json转换成正常的text格式。

pg_store_plans_jsonplan
:类似同上,只是将短格式的json转换成正常的json格式表示。

pg_store_plans_xmlplan
:类似同上,将短格式的json转换成xml格式表示。

pg_store_plans_yamlplan
:类似同上,将短格式的json转换yaml格式表示。

控制参数

pg_store_plans.max
: pg_store_plans视图中的最大行数,默认值为1000,不频繁使用的执行计划的信息将被丢弃。该参数只能在服务器启动时设置。

pg_store_plans.track
: 控制对哪些语句进行计数。指定top
跟踪顶级语句(由客户端直接发出的语句),all
还跟踪嵌套语句(例如函数内调用的语句),指定none
以禁用语句统计信息收集。默认值为top
。只有超级用户才能更改此设置。

pg_store_plans.plan_format
:控制pg_store_plans中存储执行计划的格式。text
为默认值并以普通文本表示形式显示,json
xml
yaml
以相应格式显示。raw
提供的是内部格式。

pg_store_plans.min_duration
: 语句最小执行时间,以毫秒为单位。如果设置为0(默认值),则记录所有执行计划。只有超级用户才能更改此设置。

pg_store_plans.log_analyze
: 保存的执行计划中包含EXPLAIN ANALYZE 输出,而不仅仅是EXPLAIN输出。这个参数默认值为off。

pg_store_plans.log_buffers
: 保存的执行计划中包含EXPLAIN (ANALYZE, BUFFERS)输出,而不仅仅是EXPLAIN输出,该参数默认值为off。

pg_store_plans.log_timing
: 设置为 false 会禁用记录实际时间。在某些系统上,重复读取系统时钟的开销会显著降低查询的速度,因此当只需要实际行数而不是每个执行节点的精确执行时间时,将此参数设置为FALSE可能很有用。当pg_store_plans.log_analyze
设置为TRUE时,始终测量整个语句的运行时间。它默认为TRUE。

pg_store_plans.log_triggers
: 在记录的执行计划中包含触发器执行统计信息。除非打开pg_store_plans.log_analyze,
否则此参数无效。

pg_store_plans.verbose
: 保存的执行计划中包含EXPLAIN VERBOSE输出,而不仅仅是 EXPLAIN 输出。该参数默认值为off。

pg_store_plans.save
: 指定是否在服务器关闭时保存计划统计信息,默认值为on。如果设置为off,则统计信息不会在关闭时保存,也不会在服务器启动时重新加载。此参数只能在postgresql.conf
文件或服务器命令行中设置。

使用这个插件需要与pg_store_plans.max
成比例的额外共享内存 。请注意,无论何时加载模块,都会消耗此内存,即使 pg_store_plans.track
设置为none

测试试用

先看下我们当前的设置情况。

test=# SELECT name, setting FROM pg_settings WHERE name LIKE 'pg_store_plans.%';            name             | setting -----------------------------+--------- pg_store_plans.log_analyze  | off pg_store_plans.log_buffers  | off pg_store_plans.log_timing   | on pg_store_plans.log_triggers | off pg_store_plans.log_verbose  | off pg_store_plans.max          | 1000 pg_store_plans.min_duration | 0 pg_store_plans.plan_format  | text pg_store_plans.save         | on pg_store_plans.track        | top

运行pgbench来生点数据和造点执行计划。在测试之前我们先清空pg_store_plans。

test=# SELECT pg_store_plans_reset(); pg_store_plans_reset ---------------------- (1 row)pgbench -U root -p 18804 -i -s 10 --unlogged-tables test -q dropping old tables...NOTICE:  table "pgbench_accounts" does not exist, skippingNOTICE:  table "pgbench_branches" does not exist, skippingNOTICE:  table "pgbench_history" does not exist, skippingNOTICE:  table "pgbench_tellers" does not exist, skippingcreating tables...generating data...1000000 of 1000000 tuples (100%) done (elapsed 0.69 s, remaining 0.00 s)vacuuming...creating primary keys...done.

查看pg_stort_plans视图。

test=#  SELECT queryid, plan,calls FROM pg_store_plans where dbid=6673065;  queryid   |                                  plan                                  | calls ------------+------------------------------------------------------------------------+-------  329411391 | Function Scan on pg_store_plans  (cost=0.00..10.00 rows=1000 width=24) |     1 3137955951 | Result  (cost=0.00..0.01 rows=1 width=4)                               |     1 1446640908 | Insert on pgbench_branches  (cost=0.00..0.01 rows=1 width=40)         +|    10            |   ->  Result  (cost=0.00..0.01 rows=1 width=40)                        |  3770510668 | Function Scan on pg_store_plans  (cost=0.00..12.50 rows=5 width=40)   +|     1            |   Filter: (dbid = '6673065'::oid)                                      |  3126036813 | Insert on pgbench_tellers  (cost=0.00..0.01 rows=1 width=44)          +|   100            |   ->  Result  (cost=0.00..0.01 rows=1 width=44)                        |   791401476 | Function Scan on pg_store_plans  (cost=0.00..12.50 rows=5 width=24)   +|     1            |   Filter: (dbid = '6673065'::oid)                                      | 

这里可以看到记录了两个insert语句的执行计划,分别是插入pgbench_branches和pgbench_tellers。但是像pgbench_accounts这样的表就没有记录,这是因为它是copy语句而不是dml。

手动执行一条查询语句,然后查看该语句的执行计划,已经记录进去了。

SELECT abalance FROM pgbench_accounts WHERE aid=123;test=# SELECT queryid, plan,calls FROM pg_store_plans where dbid=6673065; queryid   |                                             plan                                             | calls ------------+----------------------------------------------------------------------------------------------+------- 3137955951 | Result  (cost=0.00..0.01 rows=1 width=4)                                                     |     1 1446640908 | Insert on pgbench_branches  (cost=0.00..0.01 rows=1 width=40)                               +|    10            |   ->  Result  (cost=0.00..0.01 rows=1 width=40)                                              |  3126036813 | Insert on pgbench_tellers  (cost=0.00..0.01 rows=1 width=44)                                +|   100            |   ->  Result  (cost=0.00..0.01 rows=1 width=44)                                              |  2032872830 | Index Scan using pgbench_accounts_pkey on pgbench_accounts  (cost=0.42..8.44 rows=1 width=4)+|     1            |   Index Cond: (aid = 123)   

后记

pg_store_plans
可以帮助我们实现像Oracle一样的v$sql_plan的功能。但是它同样也会消耗一定的共享内存,特别是当你max设置的比较大的时候。

最后修改时间:2021-08-27 09:24:55
文章转载自励志成为postgresql大神,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论