插件plsql_plprofiler是KingbaseES 的一个扩展插件。plsql_plprofiler插件支持收集分析器(性能)数据以提高性能或确定PL/SQL应用程序的代码覆盖率。应用程序可以使用代码覆盖率数据来集中他们的增量测试工作。
此插件可以为所有PL/SQL单元生成分析信息,此信息包括每行的执行次数、执行该行所花费的总时间以及该行在多次执行中消耗的最长时间。
plsql_plprofiler中的监听方式分为本地监控、全局监控、以及指定会话监控三种。其中,本地监控模式是指只对当前session运行的对象进行监控;全局监控模式是指对所有session运行的对象进行监控;指定会话模式是指对某个指定session上运行的对象进行监控。
三种模式同时启动的情况下优先级是 全局监控模式 > 指定会话监听模式 > 本地监听模式。
plsql_plprofiler中的数据共享方式分为local和global两种。其中,local模式数据不共享,仅当前session可见;global模式数据共享,所有session可见。
插件名为 plsql_plprofiler
插件版本 V4.1
KingbaseES数据库默认将他添加到 kingbase.conf 文件的 shared_preload_libraries 中,重启数据库时自动加载。因此只需要在客户端工具执行create extension命令即可。
-- 创建插件 create extension plsql_plprofiler;
plsql_plprofiler定义了三个系统参数:
plsql_plprofiler.max_callgraphs
设置内存中存储堆栈关系的行数,默认值为20000。
plsql_plprofiler.max_functions
设置内存中可存储的对象个数,默认值为2000。
plsql_plprofiler.max_lines
设置内存中分析数据记录行数,默认值为200000。
支持profiler的对象:
支持函数分析
支持存储过程分析
支持包分析
包中支持分析的对象:包中初始化块、包中函数、包中存储过程。 包中对象的分析数据记录到包对应的oid。
支持触发器分析
支持嵌套函数分析
支持匿名块分析
匿名块的分析数据记录到oid为9999的行。
支持object type分析
object type中支持分析的对象:member方法、construct方法、static方法。 object type中分析数据记录到各个方法对应的oid。
注意
如果是重复执行同一个对象(以oid为标识),需要清理之前记录的数据。
**支持profiler的是数据信息分为三类:**PLSQL对象执行信息、PLSQL对象堆栈信息和PLSQL对象源码信息。
plsql_plprofiler获取到的执行信息如下:
对象oid
plsql_plprofiler对象的oid,包中的所有对象共用包的oid、匿名块的oid为9999、object type中的对象使用方法本身的oid。嵌套函数的oid使用祖先函数的oid。
语句所在行
0行表示该对象的整体数据。
如果PL/SQL源码在一行上,则该语句的任何执行相关数据都属于该行。
如果PL/SQL源码在多行上,则该语句的任何执行相关数据都属于该语句的第一行。
如果PL/SQL源码的一行出现多个语句时,则多条语句的任何执行相关数据都属于该行。
执行次数
记录某一行代码的执行次数。一行代码不一定是一条语句。
总共耗时
记录某一行代码的总耗时。如果是控制语句则会包括整个控制语句中包含的语句的时间。时间单位us。
最长耗时
记录某一行代码耗时最长的时间。时间单位us。
注意
当执行对象抛出异常未被捕获时,只会记录整个对象的执行时间,其他数据均为0;当执行对象抛出异常被捕获时,异常语句的数据为0,其他语句数据正常记录。
如果执行对象中有事务操作(commit/rollback),该对象记录的总耗时是事务操作前的总时间。
当开启set serverout on时,每条SQL语句都会执行dbms_output包中函数。因此执行语句时,plsql_plprofiler会记录调用dbms_output包的数据,如:调用数据clean函数后,首先清除执行数据,然后会调用dbms_ouput包中函数。
plsql_plprofiler获取到的堆栈信息如下:
堆栈
plsql_plprofiler对象的堆栈
调用次数
该对象被调用的总次数。
总耗时
该对象总花费时间。
调用对象耗时
该对象调用对象的耗时。
自身耗时
该对象除去调用其他对象的耗时。
plsql_plprofiler获取到的源码信息如下:
oid
行号
源码
由于匿名块在数据库中没有做存储,所以无法获取匿名块的源码信息。
接口函数 | 简介 |
---|---|
启停当前session分析器 | |
启停指定session分析器 | |
查看当前session分析器状态 | |
查看指定session分析器状态 | |
启停数据库级分析器 | |
查看全局监控是否启用的状态 | |
单次数据flush | |
指定时间间隔数据flush | |
查看定时迁移的状态 | |
本地数据clean | |
全局数据clean | |
查看本地的对象的执行信息 | |
查看全局的对象的执行信息 | |
查看本地已执行plsql对象 | |
查看全局已执行plsql对象 | |
查看本地的堆栈关系 | |
查看全局的堆栈关系 | |
OID转对象名 | |
查看指定对象源码信息 | |
查看整型版本信息 | |
查看字符串版本信息 | |
查看行数是否过界 |
68.4.2.1. pl_profiler_set_enabled_local 函数
功能描述
开启或关闭当前会话分析器。开启分析器则会对session中执行的所有plsql对象进行分析,并将信息进行暂存。
语法格式
FUNCTION pl_profiler_set_enabled_local(enabled bool) RETURNS bool
参数说明:
输入为true则为启用分析器,输入为false则为关闭分析器。
输出说明
返回值有三种:true、false、null(空),如果输入和输出一致,表示程序执行成功;反之,程序执行失败。
68.4.2.2. pl_profiler_set_enabled_pid 函数
功能描述
开启或关闭指定会话分析器。开启分析器则会对session中执行的所有plsql对象进行分析,并将信息进行暂存。
语法格式
FUNCTION pl_profiler_set_enabled_pid(pid int4) RETURNS bool
参数说明:
需要监控进程的PID,每次只有一个session可以通过本方式启停分析器。输入PID为有效seesion 时,启动分析器。PID为无效session时,则关闭已启动的监听器。
输出说明
输入进程号非0则返回true,输入进程号为0则返回为false。
68.4.2.3. pl_profiler_get_enabled_local 函数
功能描述
查看当前会话分析器状态。
语法格式
FUNCTION pl_profiler_get_enabled_local() RETURNS bool
输出说明
返回值为true则监控状态为启用,返回值为false则监控为未启用。
68.4.2.4. pl_profiler_get_enabled_pid 函数
功能描述
查看指定会话级分析器状态。
语法格式
FUNCTION pl_profiler_get_enabled_pid() RETURNS bool
输出说明
返回值为true则监控状态为启用,返回值为false则监控为未启用。
68.4.2.5. pl_profiler_set_enabled_global 函数
功能描述
开启或关闭数据库级分析器。开启全局监控则会对所有会话执行的所有plsql对象进行分析,并将信息进行暂存。
语法格式
FUNCTION pl_profiler_set_enabled_global(enabled bool) RETURNS bool
参数说明:
输入为true则为启用全局监控,输入为false则为关闭全局监控。
输出说明
返回值有三种:true、false、null(空)。如果输入和输出一致,表示程序执行成功;反之,程序执行失败。
68.4.2.6. pl_profiler_get_enabled_global 函数
功能描述
查看全局监控是否启用的状态。
语法格式
FUNCTION pl_profiler_get_enabled_global() RETURNS bool
输出说明
返回值为true则监控状态为启用,返回值为false则监控状态为未启用。
68.4.2.7. pl_profiler_collect_data 函数
功能描述
将本地数据迁移到全局数据中。数据迁移后,本地的分析数据会被清除(执行次数,最长时间,总共花费时间)。
语法格式
FUNCTION pl_profiler_collect_data() RETURNS int4
输出说明
返回值:返回为-1则表示没有将plprofiler插件加载到shared_preload_libraries中,返回为0则表示迁移成功或没有数据需要迁移。
注意
如果全局数据区内容已满,此时本地数据不会完全迁移到全局数据区中,同时系统报错内存已超出。
68.4.2.8. pl_profiler_set_collect_interval 函数
功能描述
设置时间(秒级单位)自动将本地数据迁移到全局数据中。数据迁移后,本地的分析数据会被清除(执行次数,最长时间,总共花费时间)。
语法格式
FUNCTION pl_profiler_set_collect_interval(seconds int4) RETURNS bool
输入说明:
输入为秒级单位。
输出说明
当输入seconds为负数,返回值为t,无实际意义;
当输入seconds为0时,返回值为f,无实际意义;
当输入seconds为大于0时,返回值为t,设置迁移时间有效。
如果当前事务是:XACT_EVENT_COMMIT,、XACT_EVENT_PARALLEL_COMMIT、XACT_EVENT_ABORT、XACT_EVENT_PARALLEL_ABORT且已设置有效的迁移时间,则立即将本地数据迁移到全局数据中。
68.4.2.9. pl_profiler_get_collect_interval 函数
功能描述
查看是否定时迁移的状态。
语法格式
FUNCTION pl_profiler_get_collect_interval() RETURNS bool
输出说明
返回值:根据是否定时的设置返回对应状态。如果之前用户设置的定时时间非0则该函数返回t,如果之前设置定时时间为0则返回f。
68.4.2.10. pl_profiler_reset_local 函数
功能描述
清除本地存储的所有数据信息。
语法格式
FUNCTION pl_profiler_reset_local() RETURNS void
68.4.2.11. pl_profiler_reset_shared 函数
功能描述
清除全局存储的所有数据信息。
语法格式
FUNCTION pl_profiler_reset_shared() RETURNS void
68.4.2.12. pl_profiler_linestats_local 函数
功能描述
查看本地的对象的执行信息
语法格式
FUNCTION pl_profiler_linestats_local( OUT func_oid oid, OUT line_number int8, OUT exec_count int8, OUT total_time int8, OUT longest_time int8 ) RETURNS SETOF record AS 'MODULE_PATHNAME' LANGUAGE C ROWS 1000000;
输出说明
返回一个具有以下结构的数据集合:
func_oid:类型为oid,表示已经监控的对象oid;
line_number:类型为int8,表示当前语句行号;
exec_count:类型为int8,表示执行次数;
total_time:类型为int8,表示当前语句执行总时长;
longest_time:类型为int8,表示当前语句执行的最长一次时长。
注意
line_number会根据源码(系统表中存储源码)进行计算,line_number为0的行表示了当前对象的执行次数,总共执行时长,最长一次执行时间。
68.4.2.13. pl_profiler_linestats_shared 函数
功能描述
查看全局的对象的执行信息
语法格式
FUNCTION pl_profiler_linestats_shared( OUT func_oid oid, OUT line_number int8, OUT exec_count int8, OUT total_time int8, OUT longest_time int8 ) RETURNS SETOF record AS 'MODULE_PATHNAME' LANGUAGE C ROWS 1000000;
输出说明
返回一个具有以下结构的多行:
func_oid:类型为oid,表示已经监控的对象oid;
line_number:类型为int8,表示当前语句行号;
exec_count:类型为int8,表示执行次数;
total_time:类型为int8,表示当前语句执行总时长;
longest_time:类型为int8,表示当前语句执行的最长一次时长。
注意
line_number会根据源码(系统表中存储源码)进行计算,line_number为0的行表示了当前对象的执行次数,总共执行时长,最长一次执行时间。
68.4.2.14. pl_profiler_func_oids_local 函数
功能描述
查看本地已执行plsql对象。
语法格式
FUNCTION pl_profiler_func_oids_local() RETURNS oid[]
输出说明
返回值:返回plsql对象的oid数组。
68.4.2.15. pl_profiler_func_oids_shared 函数
功能描述
查看全局已执行plsql对象
语法格式
FUNCTION pl_profiler_func_oids_shared() RETURNS oid[]
输出说明
返回值:返回plsql对象的oid数组。
68.4.2.16. pl_profiler_callgraph_local 函数
功能描述
查看本地已监控的plsql对象及子程序调用信息。
语法格式
FUNCTION pl_profiler_callgraph_local( OUT stack oid[], OUT call_count int8, OUT us_total int8, OUT us_children int8, OUT us_self int8 ) RETURNS SETOF record AS 'MODULE_PATHNAME' LANGUAGE C ROWS 1000000;
输出说明
返回一个具有以下结构的多行:
stack:类型为oid[],表示已经监控的对象oid数组,能够表明监控对象的堆栈关系;
call_count:类型为int8,表示当前对象调用次数;
us_total:类型为int8,表示执行该对象总共花费时间;
us_children:类型为int8,表示执行该对象中的子程序总共花费时间;
us_self:类型为int8,表示执行该对象除子程序外,对象本身语句花费时间。
注意
如果调用的plsql对象中没有调用子程序,则调用pl_profiler_callgraph_local函数只有一行信息;如果调用的plsql对象中存在调用子程序,则调用pl_profiler_callgraph_local函数除了显示调用的plsql对象,还有显示子程序的性能数据信息,并且子程序性能数据信息中的stack会显示堆栈关系,如{16513,16515,16514},其中16513为调用的plsql对象,16515为16513的子程序,16514为61515的子程序。
68.4.2.17. pl_profiler_callgraph_shared 函数
功能描述
查看全局的plsql对象及子程序调用信息。
语法格式
FUNCTION pl_profiler_callgraph_shared( OUT stack oid[], OUT call_count int8, OUT us_total int8, OUT us_children int8, OUT us_self int8 ) RETURNS SETOF record AS 'MODULE_PATHNAME' LANGUAGE C ROWS 1000000;
输出说明
返回一个具有以下结构的多行:
stack:类型为oid[],表示已经监控的对象oid数组,能够表明监控对象的堆栈关系;
call_count:类型为int8,表示当前对象调用次数;
us_total:类型为int8,表示执行该对象总共花费时间;
us_children:类型为int8,表示执行该对象中的子程序总共花费时间;
us_self:类型为int8,表示执行该对象除子程序外,对象本身语句花费时间。
注意
如果调用的plsql对象中没有调用子程序,则调用pl_profiler_callgraph_shared函数只有一行信息;如果调用的plsql对象中存在调用子程序,则调用pl_profiler_callgraph_shared函数除了显示调用的plsql对象,还有显示子程序的性能数据信息,并且子程序性能数据信息中的stack会显示堆栈关系,如{16513,16515,16514},其中16513为调用的plsql对象,16515为16513的子程序,16514为61515的子程序。
68.4.2.18. pl_profiler_get_stack 函数
功能描述
将OID转成对象名称及当前模式名信息。
语法格式
FUNCTION pl_profiler_get_stack(stack oid[]) RETURNS text[] AS 'MODULE_PATHNAME' LANGUAGE C STRICT;
输入说明:
stack为一个oid的数组,通常为pl_profiler_func_oids_shared接口或pl_profiler_func_oids_local接口的返回值。
输出说明
将传入的oid转化为模式+函数名称的text数组。
注意
如果传入的oid对应的模式名或对象名称不存在,则text数组中会返回"
68.4.2.19. pl_profiler_funcs_source 函数
功能描述
查看指定对象源码信息。
语法格式
FUNCTION pl_profiler_funcs_source( IN func_oids oid[], OUT func_oid oid, OUT line_number int8, OUT source text ) RETURNS SETOF record AS 'MODULE_PATHNAME' LANGUAGE C ROWS 1000000 STRICT;
输入说明:
func_oids:传入的一个对象oid数组。
输出说明
返回值:通过传入对象oid数组,输出以下结构的多行:
func_oid:对象oid;
line_number:当前行号;
source:当前行号对应源码。
注意
系统会自动增加一行line_number为0的行,并且suorce为“-- Line 0”。如果不能查询到输入oid的源码,则系统报错。
不能查询匿名块的源码。
68.4.2.20. pl_profiler_version 函数
功能描述
查看整型版本信息。
语法格式
FUNCTION pl_profiler_version() RETURNS integer
输出说明
返回值:输出当前的版本值。
68.4.2.21. pl_profiler_versionstr 函数
功能描述
查看字符串版本信息。
语法格式
FUNCTION pl_profiler_versionstr() RETURNS text
输出说明
返回值:输出当前版本的字符串信息。
68.4.2.22. pl_profiler_lines_overflow 函数
功能描述
查看存储对象具体信息结构的行数是否过界。
语法格式
FUNCTION pl_profiler_lines_overflow() RETURNS bool
输出说明
返回值:如果profiler_linestats结构内存过界,则返回t,否则返回f。
Profiler每次运行的的过程应该包括以下步骤:
启动分析器
运行应用程序
关闭分析器
数据flush
分析数据
用户在使用profiler前,需要创建profiler扩展。
profiler收集数据,通过调用pl_profiler_set_enabled_local函数来控制分析器的启动或者停止。 分析器收集到的数据存储在内存中,如果用户断开连接,分析器将不会自动存储数据,需要显示调用pl_profiler_collect_data函数将数据刷到共享内存。 需要注意的是,目前profiler数据不会落盘存储,所以一旦重启数据库,分析数据将会丢失。
--创建扩展 create extension plsql_plprofiler; --创建对象 create table t1(i int); \set SQLTERM / create or replace procedure p1() as begin raise notice 'this is procedure p1'; for i in 1..1000 loop insert into t1 values(99999); end loop; if 1000 < 100 then null; else null; end if; end; / create or replace procedure p2() as begin raise notice 'this is procedure p2'; p1(); end; / create or replace procedure p3() as begin raise notice 'this is procedure p3'; p2(); if 1 < 100 then raise notice '1 < 100'; end if; end; / \set SQLTERM ; --清理本地数据 select pl_profiler_reset_local(); --清理全局数据 select pl_profiler_reset_shared(); --启动分析器 select pl_profiler_set_enabled_global(true); --运行应用程序 call p3(); --关闭分析器 select pl_profiler_set_enabled_global(false); --数据flush select pl_profiler_collect_data(); --分析数据 ----查看已记录的对象(可选) select * from pl_profiler_func_oids_shared(); ----oid转对象名(可选) select * from pl_profiler_get_stack(pl_profiler_func_oids_shared());
\set SQLTERM ; --清理本地数据 select pl_profiler_reset_local(); pl_profiler_reset_local ------------------------- (1 row) --清理全局数据 select pl_profiler_reset_shared(); pl_profiler_reset_shared -------------------------- (1 row) --启动分析器 select pl_profiler_set_enabled_global(true); pl_profiler_set_enabled_global -------------------------------- t (1 row) --运行应用程序 call p3(); NOTICE: this is procedure p3 NOTICE: this is procedure p2 NOTICE: this is procedure p1 NOTICE: 1 < 100 CALL --关闭分析器 select pl_profiler_set_enabled_global(false); pl_profiler_set_enabled_global -------------------------------- f (1 row) --数据flush select pl_profiler_collect_data(); pl_profiler_collect_data -------------------------- 0 (1 row) --分析数据 ----查看已记录的对象(可选) select * from pl_profiler_func_oids_shared(); pl_profiler_func_oids_shared ------------------------------ {16793,16792,16794} (1 row) ----oid转对象名(可选) select * from pl_profiler_get_stack(pl_profiler_func_oids_shared()); pl_profiler_get_stack --------------------------------------------------------------------------- {"public.p2() oid=16793","public.p1() oid=16792","public.p3() oid=16794"} (1 row)
----查看数据(可选) select func_oid, func_oid::regproc as funcname,line_number, source from pl_profiler_funcs_source(pl_profiler_func_oids_shared());
----查看数据(可选) select func_oid, func_oid::regproc as funcname,line_number, source from pl_profiler_funcs_source(pl_profiler_func_oids_shared()); func_oid | funcname | line_number | source ----------+----------+-------------+----------------------------------------------------- 16793 | p2 | 0 | -- Line 0 16793 | p2 | 1 | 16793 | p2 | 2 | begin 16793 | p2 | 3 | raise notice 'this is procedure p2'; 16793 | p2 | 4 | p1(); 16793 | p2 | 5 | end 16792 | p1 | 0 | -- Line 0 16792 | p1 | 1 | 16792 | p1 | 2 | begin 16792 | p1 | 3 | raise notice 'this is procedure p1'; 16792 | p1 | 4 | for i in 1..1000 loop 16792 | p1 | 5 | insert 16792 | p1 | 6 | into 16792 | p1 | 7 | t1 16792 | p1 | 8 | values(99999); 16792 | p1 | 9 | end loop; 16792 | p1 | 10 | if 1000 < 100 then null; else null; end if; 16792 | p1 | 11 | end 16794 | p3 | 0 | -- Line 0 16794 | p3 | 1 | 16794 | p3 | 2 | begin 16794 | p3 | 3 | raise notice 'this is procedure p3'; 16794 | p3 | 4 | p2(); 16794 | p3 | 5 | if 1 < 100 then 16794 | p3 | 6 | raise notice '1 < 100'; 16794 | p3 | 7 | end if; 16794 | p3 | 8 | end (27 rows)
----查看对象调用关系 select * from pl_profiler_callgraph_shared(); stack | call_count | us_total | us_children | us_self
---------------------+------------+----------+-------------+---------
{16794,16793} | 1 | 8175 | 7990 | 185 {16794,16793,16792} | 1 | 7990 | 0 | 7990 {16794} | 1 | 8473 | 8175 | 298
(3 rows)
-----源码和分析数据对应该系 SELECT L.func_oid::regproc as funcname, L.func_oid as func_oid, L.line_number, sum(L.exec_count)::bigint AS exec_count, sum(L.total_time)::bigint AS total_time, max(L.longest_time)::bigint AS longest_time, S.source FROM pl_profiler_linestats_shared() L JOIN pl_profiler_funcs_source(pl_profiler_func_oids_shared) S ON S.func_oid = L.func_oid AND S.line_number = L.line_number GROUP BY L.func_oid, L.line_number, S.source ORDER BY L.func_oid, L.line_number; funcname | func_oid | line_number | exec_count | total_time | longest_time | source ----------+----------+-------------+------------+------------+--------------+----------------------------------------------------- p1 | 16792 | 0 | 1 | 7990 | 7990 | -- Line 0 p1 | 16792 | 1 | 0 | 0 | 0 | p1 | 16792 | 2 | 1 | 7989 | 7989 | begin p1 | 16792 | 3 | 1 | 14 | 14 | raise notice 'this is procedure p1'; p1 | 16792 | 4 | 1 | 7830 | 7830 | for i in 1..1000 loop p1 | 16792 | 5 | 1000 | 7181 | 314 | insert p1 | 16792 | 6 | 0 | 0 | 0 | into p1 | 16792 | 7 | 0 | 0 | 0 | t1 p1 | 16792 | 8 | 0 | 0 | 0 | values(99999); p1 | 16792 | 9 | 0 | 0 | 0 | end loop; p1 | 16792 | 10 | 2 | 0 | 0 | if 1000 < 100 then null; else null; end if; p1 | 16792 | 11 | 0 | 0 | 0 | end p2 | 16793 | 0 | 1 | 8175 | 8175 | -- Line 0 p2 | 16793 | 1 | 0 | 0 | 0 | p2 | 16793 | 2 | 1 | 8174 | 8174 | begin p2 | 16793 | 3 | 1 | 22 | 22 | raise notice 'this is procedure p2'; p2 | 16793 | 4 | 1 | 8151 | 8151 | p1(); p2 | 16793 | 5 | 0 | 0 | 0 | end p3 | 16794 | 0 | 1 | 8473 | 8473 | -- Line 0 p3 | 16794 | 1 | 0 | 0 | 0 | p3 | 16794 | 2 | 1 | 8471 | 8471 | begin p3 | 16794 | 3 | 1 | 50 | 50 | raise notice 'this is procedure p3'; p3 | 16794 | 4 | 1 | 8365 | 8365 | p2(); p3 | 16794 | 5 | 1 | 52 | 52 | if 1 < 100 then p3 | 16794 | 6 | 1 | 13 | 13 | raise notice '1 < 100'; p3 | 16794 | 7 | 0 | 0 | 0 | end if; p3 | 16794 | 8 | 0 | 0 | 0 | end (27 rows)
卸载plsql_plprofiler插件接口,只需要执行以下命令:
-- 删除插件 drop extension plsql_plprofiler;
若该插件有升级版本则通过 alter extension 来升级插件。
示例,由 4.1 升级到 4.2:
ALTER EXTENSION plsql_plprofiler UPDATE TO '4.2';