PostgreSQL 函数调试、诊断、优化 & auto_explain

本文涉及的产品
云数据库 RDS SQL Server,独享型 2核4GB
云数据库 RDS MySQL Serverless,0.5-2RCU 50GB
云原生数据库 PolarDB MySQL 版,通用型 2核4GB 50GB
简介:
+关注继续查看

PostgreSQL 函数调试 & auto_explain

作者

digoal

日期

2016-11-21

标签

PostgreSQL , pldebugger , 函数调试 , auto_explain


背景

PostgreSQL的服务端编程能力比较强,不仅支持像java, R, python这种流行的编程语言,另外还内置了一个与Oracle plsql功能几乎一致的plpgsql语言。

对于开发人员来说,有了语言还不够的,调试是非常常见的需求。

所以PostgreSQL的函数语言怎么调试呢,使用pldebugger即可。

pldebugger的安装

https://git.postgresql.org/gitweb/?p=pldebugger.git;a=summary

plpgsql调试方法

https://www.pgadmin.org/docs/1.22/debugger.html

除了函数的调试,还有函数内SQL的执行计划如何观测。

auto_explain

auto_explain 一个比较给力的模块.

用于向日志输出超出定义阈值执行时间的SQL的执行计划。更强大的是可以选择是否输出内嵌SQL的执行计划(如函数中的SQL).

auto_explain的输出更详细的解释可以参考EXPLAIN的解释。或者man EXPLAIN.

SYNOPSIS  
       EXPLAIN [ ( option [, ...] ) ] statement  
       EXPLAIN [ ANALYZE ] [ VERBOSE ] statement  

       where option can be one of:  

           ANALYZE [ boolean ]  
           VERBOSE [ boolean ]  
           COSTS [ boolean ]  
           BUFFERS [ boolean ]  
           FORMAT { TEXT | XML | JSON | YAML }  

开启方式有两种 :

1. 通过配置文件postgresql.conf

需要重启数据库。

2. 通过LOAD 'auto_explain';

这个只在当前SESSION生效,不需要重启数据库, 需要超级用户权限。

其实还可以设置local_preload_libraries,session_preload_libraries参数来启用。

例子

1. 修改配置文件:

shared_preload_libraries = 'auto_explain'  

新增如下配置:

auto_explain.log_min_duration = 0  # 为了方便查看,这里把时间设置为0,所有SQL都会被auto_explain捕获输出.实际使用的时候适当调大。如 100ms  
auto_explain.log_analyze = true  

# 以下可选  
auto_explain.log_verbose = true  
auto_explain.log_buffers = true  
auto_explain.log_nested_statements = true  

然后重启数据库.重启时会输出一个LOG

postgres@db5-> LOG:  loaded library "$libdir/auto_explain.so"  

下面来执行几条SQL

postgres@db5-> psql -h 127.0.0.1 digoal digoal  
psql (9.1beta1)  
Type "help" for help.  

digoal=> select * from tbl_user_info limit 2;  
 id | firstname | lastname |   corp     
----+-----------+----------+----------  
  1 | zhou1     | digoal1  | sky-mobi  
  2 | zhou2     | digoal2  | sky-mobi  
(2 rows)  

digoal=> select count(*) from tbl_test;  
  count    
---------  
 1000100  
(1 row)  

日志输出

2011-06-08 15:19:14.390 CST,"digoal","digoal",13789,"127.0.0.1:59549",4def2270.35dd,1,"SELECT",2011-06-08 15:19:12 CST,2/18,0,LOG,00  
000,"duration: 0.040 ms  plan:  
Query Text: select * from tbl_user_info limit 2;  
Limit  (cost=0.00..0.04 rows=2 width=31) (actual time=0.020..0.023 rows=2 loops=1)  
  Output: id, firstname, lastname, corp  
  Buffers: shared hit=1  
  ->  Seq Scan on public.tbl_user_info  (cost=0.00..183.00 rows=10000 width=31) (actual time=0.014..0.015 rows=2 loops=1)  
        Output: id, firstname, lastname, corp  
        Buffers: shared hit=1",,,,,,,,,"psql"  
2011-06-08 15:19:17.902 CST,"digoal","digoal",13789,"127.0.0.1:59549",4def2270.35dd,2,"SELECT",2011-06-08 15:19:12 CST,2/19,0,LOG,00  
000,"duration: 1232.064 ms  plan:  
Query Text: select count(*) from tbl_test;  
Aggregate  (cost=16927.25..16927.26 rows=1 width=0) (actual time=1232.044..1232.045 rows=1 loops=1)  
  Output: count(*)  
  Buffers: shared hit=4426  
  ->  Seq Scan on public.tbl_test  (cost=0.00..14427.00 rows=1000100 width=0) (actual time=0.015..626.872 rows=1000100 loops=1)  
        Output: id  
        Buffers: shared hit=4426",,,,,,,,,"psql"  

2. 通过LOAD 'auto_explain' ;

这个只在当前SESSION生效,不需要重启数据库, 需要超级用户权限。

首先先恢复postgresql.conf的配置,去除前面的配置.然后重启数据库.

普通用户不允许加载auto_explain模块.(普通用户只允许加载$libdir/plugins目录下的模块,但是auto_explain即使拷贝到这个目录也不行)

digoal=> load 'auto_explain';  
ERROR:  access to library "auto_explain" is not allowed  
digoal=> \c digoal postgres  
You are now connected to database "digoal" as user "postgres".  
digoal=# load 'auto_explain';  
LOAD  
digoal=# set auto_explain.log_min_duration=0;  
SET  
digoal=# select * from tbl_user_info limit 2;  
 id | firstname | lastname |   corp     
----+-----------+----------+----------  
  1 | zhou1     | digoal1  | sky-mobi  
  2 | zhou2     | digoal2  | sky-mobi  
(2 rows)  

查看日志

2011-06-08 15:25:33.361 CST,"postgres","digoal",13968,"127.0.0.1:15445",4def23d5.3690,1,"SELECT",2011-06-08 15:25:09 CST,2/9,0,LOG,0  
0000,"duration: 0.048 ms  plan:  
Query Text: select * from tbl_user_info limit 2;  
Limit  (cost=0.00..0.04 rows=2 width=31)  
  ->  Seq Scan on tbl_user_info  (cost=0.00..183.00 rows=10000 width=31)",,,,,,,,,"psql"  

连接到digoal用户,这个LOAD的模块消失(后面会有解释).

digoal=# load 'auto_explain';  
LOAD  
digoal=# set session auto_explain.log_min_duration=0;  
SET  
digoal=# \c digoal digoal  
You are now connected to database "digoal" as user "digoal".  
digoal=> select * from tbl_user_info limit 3;  
 id | firstname | lastname |   corp     
----+-----------+----------+----------  
  1 | zhou1     | digoal1  | sky-mobi  
  2 | zhou2     | digoal2  | sky-mobi  
  3 | zhou3     | digoal3  | sky-mobi  
(3 rows)  

查看日志,没有auto_explain的输出.即使这里使用了session来定义这个参数的有效范围。

原因是session重新分配了,因为\c digoal digoal 相当于重新连接,backend也重新生成,如下.

digoal=> select * from pg_stat_activity;  
 datid | datname | procpid | usesysid | usename | application_name | client_addr | client_hostname | client_port |         backend_s  
tart         |          xact_start           |          query_start          | waiting |          current_query            
-------+---------+---------+----------+---------+------------------+-------------+-----------------+-------------+------------------  
-------------+-------------------------------+-------------------------------+---------+---------------------------------  
 16430 | digoal  |   14155 |    16423 | digoal  | psql             | 127.0.0.1   |                 |        5959 | 2011-06-08 15:31:  
32.794906+08 | 2011-06-08 15:31:40.616346+08 | 2011-06-08 15:31:40.616346+08 | f       | select * from pg_stat_activity;  
(1 row)  

digoal=> \c digoal postgres  
You are now connected to database "digoal" as user "postgres".  
digoal=# select * from pg_stat_activity;  
 datid | datname | procpid | usesysid | usename  | application_name | client_addr | client_hostname | client_port |         backend_  
start         |          xact_start           |          query_start          | waiting |          current_query            
-------+---------+---------+----------+----------+------------------+-------------+-----------------+-------------+-----------------  
--------------+-------------------------------+-------------------------------+---------+---------------------------------  
 16430 | digoal  |   14161 |       10 | postgres | psql             | 127.0.0.1   |                 |        5961 | 2011-06-08 15:31  
:43.937297+08 | 2011-06-08 15:31:45.178005+08 | 2011-06-08 15:31:45.178005+08 | f       | select * from pg_stat_activity;  
(1 row)  

那么如何避免这样的情况发生呢? 答案是使用set role .

digoal=# load 'auto_explain';  
LOAD  
digoal=# set session auto_explain.log_min_duration=0;  
SET  
digoal=# set role digoal;  
SET  
digoal=> select * from tbl_user_info limit 3;  
 id | firstname | lastname |   corp     
----+-----------+----------+----------  
  1 | zhou1     | digoal1  | sky-mobi  
  2 | zhou2     | digoal2  | sky-mobi  
  3 | zhou3     | digoal3  | sky-mobi  
(3 rows) 

再看日志,已经有了 :

2011-06-08 15:29:30.011 CST,"postgres","digoal",14062,"127.0.0.1:5939",4def24bc.36ee,1,"SELECT",2011-06-08 15:29:00 CST,2/29,0,LOG,0  
0000,"duration: 0.031 ms  plan:  
Query Text: select * from tbl_user_info limit 3;  
Limit  (cost=0.00..0.05 rows=3 width=31)  
  ->  Seq Scan on tbl_user_info  (cost=0.00..183.00 rows=10000 width=31)",,,,,,,,,"psql"  

如果你不想看日志这么麻烦,想在client直接显示,也很方便,设置client_min_messages='log'就可以看到auto explain的输出了。

set client_min_messages='log';
set auto_explain.log_min_duration = 0;
set auto_explain.log_analyze = true;
set auto_explain.log_verbose = true;
set auto_explain.log_buffers = true;
set auto_explain.log_nested_statements = true;

postgres=# do language plpgsql $$        
declare
begin
perform 1 from pg_class where oid=1;
end;
$$;

LOG:  duration: 0.008 ms  plan:
Query Text: SELECT 1 from pg_class where oid=1
Index Only Scan using pg_class_oid_index on pg_catalog.pg_class  (cost=0.27..1.29 rows=1 width=4) (actual time=0.006..0.006 rows=0 loops=1)
  Output: 1
  Index Cond: (pg_class.oid = '1'::oid)
  Heap Fetches: 0
  Buffers: shared hit=2
DO

参考

1. man LOAD

2. man EXPLAIN

3. http://www.postgresql.org/docs/9.6/static/auto-explain.html

相关实践学习
使用PolarDB和ECS搭建门户网站
本场景主要介绍基于PolarDB和ECS实现搭建门户网站。
阿里云数据库产品家族及特性
阿里云智能数据库产品团队一直致力于不断健全产品体系,提升产品性能,打磨产品功能,从而帮助客户实现更加极致的弹性能力、具备更强的扩展能力、并利用云设施进一步降低企业成本。以云原生+分布式为核心技术抓手,打造以自研的在线事务型(OLTP)数据库Polar DB和在线分析型(OLAP)数据库Analytic DB为代表的新一代企业级云原生数据库产品体系, 结合NoSQL数据库、数据库生态工具、云原生智能化数据库管控平台,为阿里巴巴经济体以及各个行业的企业客户和开发者提供从公共云到混合云再到私有云的完整解决方案,提供基于云基础设施进行数据从处理、到存储、再到计算与分析的一体化解决方案。本节课带你了解阿里云数据库产品家族及特性。
目录
相关文章
|
1月前
|
SQL 关系型数据库 测试技术
沉浸式学习PostgreSQL|PolarDB 20: 学习成为数据库大师级别的优化技能
在上一个实验《沉浸式学习PostgreSQL|PolarDB 19: 体验最流行的开源企业ERP软件 odoo》 中, 学习了如何部署odoo和polardb|pg. 由于ODOO是非常复杂的ERP软件, 对于关系数据库的挑战也非常大, 所以通过odoo业务可以更快速提升同学的数据库优化能力, 发现业务对数据库的使用问题(如索引、事务对锁的运用逻辑问题), 数据库的代码缺陷, 参数或环境配置问题, 系统瓶颈等.
635 0
|
4月前
|
存储 缓存 NoSQL
[译]解锁TOAST的秘密:如何优化PostgreSQL的大型列存储以最佳性能和可扩展性
[译]解锁TOAST的秘密:如何优化PostgreSQL的大型列存储以最佳性能和可扩展性
74 0
|
5月前
|
SQL 存储 缓存
数据库内核那些事|深度解析PolarDB DDL锁的优化和演进
DDL是数据库所有SQL操作中最繁重的一种,本文总结介绍了云原生数据库PolarDB中DDL全链路MDL锁治理的经验和进展,持续优化用户的使用体验,为用户打造最佳的云原生数据库。
|
6月前
|
SQL 关系型数据库 API
【PostgreSQL】PostgreSQL扩展:pg_stat_statements 优化SQL
【PostgreSQL】PostgreSQL扩展:pg_stat_statements 优化SQL
109 0
|
10月前
|
监控 关系型数据库 PostgreSQL
|
存储 监控 Kubernetes
【实操系列】 AnalyticDB PostgreSQL发布实例暂停功能,助力成本优化
本文将对AnalyticDB PostgreSQL产品的暂停功能以及其背后的实现机制和最佳实践做详细介绍。
988 3
【实操系列】 AnalyticDB PostgreSQL发布实例暂停功能,助力成本优化
|
存储 关系型数据库 MySQL
PolarDB B-tree 并发控制优化
PolarDB 解决了 InnoDB 在 B-tree 并发控制上的限制,解决 index lock 竞争问题,并支持了 latch coupling。
238 0
PolarDB B-tree 并发控制优化
|
SQL 缓存 运维
先入为主的PostgreSQL“递归性能问题”优化
收到运维告警,数据库磁盘容量100%,一段时间后又降了下去,使用该数据库的服务是因为人员变动后流转到我手里维护的,当时听说过因为PostgreSQL问题进行了对应优化,优化前的表现也是类似将数据库临时表空间占满,所以我也想着从这方面入手排查,后续排查确实也验证了我的猜想。
1034 0
先入为主的PostgreSQL“递归性能问题”优化
|
SQL 关系型数据库 Java
AnalyticDB PostgreSQL新功能发布,内核及SQL诊断与优化能力双双升级
新版本内核提升排序及查询能力,支持增量自动排序等核心功能。 控制台进一步优化智能诊断与能力服务,提供SQL分布统计及执行计划分析
AnalyticDB PostgreSQL新功能发布,内核及SQL诊断与优化能力双双升级
|
SQL 存储 弹性计算
年度发布解读 | PolarDB for MySQL:DDL的优化和演进
期待未来用户执行DDL操作像执行简单查询一样淡定坦然,PolarDB内核团队将始终如一地为用户打造最佳的云原生关系型数据库管理系统。
722 0
年度发布解读 | PolarDB for MySQL:DDL的优化和演进
相关产品
云原生数据库 PolarDB
推荐文章
更多