当PL/SQL函数或存储过程变得复杂时,您可能会发现难以定位其中的性能瓶颈。POLAR_PLSQL_PROFILER插件能够监控并记录PL/SQL代码的执行细节,帮助您精确分析每一行代码的执行耗时和函数间的调用关系,从而有效地进行性能优化。
功能简介
POLAR_PLSQL_PROFILER是一个性能分析工具,它通过挂载到PL/SQL的执行引擎上,在代码运行时采集性能数据。这些数据包括每行代码的执行次数、累计耗时,以及函数之间的调用层级和时间分布。分析器将采集到的数据存储在当前会话的内存中,您可以通过调用其提供的函数,以结构化数据或可视化HTML报告的形式查看分析结果。
适用范围
支持的PolarDB PostgreSQL版(兼容Oracle)的版本: Oracle语法兼容 2.0,且内核小版本需为2.0.14.19.40.0及以上。
优势
代码行级分析:能够精确到每一行源代码的执行次数和耗时,快速定位热点代码。
调用图谱:清晰展示函数间的调用关系,帮助理解整体耗时在不同函数模块间的分布。
可视化报告:支持一键生成内容详尽的HTML报告,便于分析和分享。
注意事项
性能开销:开启性能分析会带来额外的性能开销,建议仅在开发或性能诊断场景中使用,避免在生产环境中长时间开启。
会话级别:分析器采集的数据仅在当前数据库会话中有效。会话断开后,所有分析数据将丢失。
及时清理:分析结束后,请及时禁用分析器并清理数据,以释放资源并消除性能影响。
操作指南
为了更好地展示工具的使用,以下将通过两个示例进行讲解:一个用于快速入门,另一个用于演示如何分析包含子函数调用的复杂场景。
快速入门与基本分析
本示例将引导您完成一次最基本的性能分析流程。
步骤一:安装插件
在使用前,请先创建插件并启用分析器。分析器将开始监控当前会话中所有PL/SQL代码的执行。
-- 创建插件(如果尚未创建)
CREATE EXTENSION polar_plsql_profiler;步骤二:准备示例代码
执行您需要分析性能的PL/SQL函数或存储过程。以下是一个示例函数:
-- 创建一个用于测试的函数
CREATE OR REPLACE FUNCTION simple_test_func(p_times IN NUMBER)
RETURNS VARCHAR2 AS
v_result VARCHAR2(100);
BEGIN
FOR i IN 1 .. p_times LOOP
v_result := 'Iteration ' || i;
END LOOP;
RETURN v_result;
END;步骤三:启用分析器
启动分析器,启用成功将返回t。
SELECT plsql_profiler_enable();步骤四:执行目标代码
执行函数以收集数据。
SELECT simple_test_func(100);步骤五:分析性能数据
可以通过多种方式查看采集到的性能数据。
查看函数调用概览
使用plsql_profiler_show_funclist()函数可以查看本次分析中所有被调用函数的总体性能统计,帮助您快速识别耗时最长的函数。
SELECT * FROM plsql_profiler_show_funclist();返回结果如下:
func_call | func_oid_list | call_count | us_total | us_declare | us_children | us_self | time_stamp
--------------------------------------+---------------+------------+----------+------------+-------------+---------+-------------------------------
oid=21749 public.simple_test_func() | {21749} | 1 | 303 | 13 | 0 | 303 | 2026-01-05 06:01:20.619468+00查看指定函数的行级详情
当定位到目标函数后,使用plsql_profiler_show_function()可以查看该函数内部每一行代码的详细性能数据。
-- 首先,从函数列表中获取目标函数的OID数组(func_oid_list)
-- 然后,将该OID数组作为参数传入以下函数
SELECT * FROM plsql_profiler_show_function(ARRAY['...']); -- 请将'...'替换为函数OID列表示例
SELECT * FROM plsql_profiler_show_function(ARRAY[21749]);返回结果如下:
line_number | exec_count | total_time | percent | avg_time | min_time | max_time | source_code | time_stamp
-------------+------------+------------+---------+----------+----------+----------+----------------------------------------+------------
0 | 1 | 303 | 100 | 303 | 303 | 303 | ---- Function Totals ---- |
1 | 0 | 0 | 0 | 0 | 0 | 0 | v_result VARCHAR2(100); |
2 | 1 | 287 | 94.72 | 287 | 287 | 287 | BEGIN |
3 | 1 | 276 | 91.09 | 276 | 276 | 276 | FOR i IN 1 .. p_times LOOP |
4 | 100 | 145 | 47.85 | 1 | 0 | 145 | v_result := 'Iteration ' || i; |
5 | 0 | 0 | 0 | 0 | 0 | 0 | END LOOP; |
6 | 1 | 0 | 0 | 0 | 0 | 0 | RETURN v_result; |
7 | 0 | 0 | 0 | 0 | 0 | 0 | END; | 步骤六:清理资源
分析工作完成后,执行以下操作来重置数据、禁用分析器并移除插件,以避免对后续操作产生性能影响。
-- 1. (可选)重置已采集的分析数据
SELECT plsql_profiler_reset();
-- 2. 禁用分析器
SELECT plsql_profiler_disable();
-- 3. (可选)如果不再需要,可以删除插件
DROP EXTENSION polar_plsql_profiler;深入分析与定位瓶颈(含子函数调用)
本示例将模拟一个更真实的场景:一个主函数调用一个耗时的子函数。我们将演示如何通过分析器找到真正的性能瓶颈。
步骤一:安装插件
在使用前,请先创建插件并启用分析器。分析器将开始监控当前会话中所有PL/SQL代码的执行。
-- 创建插件(如果尚未创建)
CREATE EXTENSION polar_plsql_profiler;步骤二:准备示例代码
首先,创建一个包含子函数的复杂包。
-- 创建包含子函数的复杂包
CREATE OR REPLACE PACKAGE complex_pkg AS
FUNCTION calculate_tax(p_salary NUMBER) RETURN NUMBER;
PROCEDURE process_payroll(p_dept_id NUMBER);
END complex_pkg;
CREATE OR REPLACE PACKAGE BODY complex_pkg AS
FUNCTION calculate_tax(p_salary NUMBER) RETURN NUMBER IS
v_tax_amount NUMBER;
-- 子函数
FUNCTION get_tax_rate(p_income NUMBER) RETURN NUMBER IS
v_rate NUMBER;
BEGIN
IF p_income <= 50000 THEN
v_rate := 0.10;
ELSIF p_income <= 100000 THEN
v_rate := 0.20;
ELSE
v_rate := 0.30;
END IF;
RETURN v_rate;
END get_tax_rate;
BEGIN
v_tax_amount := p_salary * get_tax_rate(p_salary);
RETURN v_tax_amount;
END calculate_tax;
PROCEDURE process_payroll(p_dept_id NUMBER) IS
BEGIN
-- 执行一些操作
NULL;
END process_payroll;
END complex_pkg;步骤三:启用分析器
启动分析器,启用成功将返回t。
SELECT plsql_profiler_enable();步骤四:执行目标代码
执行函数以收集数据。
SELECT complex_pkg.calculate_tax(75000);步骤五:分析性能数据
可以通过多种方式查看采集到的性能数据。
查看函数调用概览
使用plsql_profiler_show_funclist()函数可以查看本次分析中所有被调用函数的总体性能统计,帮助您快速识别耗时最长的函数。
SELECT * FROM plsql_profiler_show_funclist();返回结果如下:
func_call | func_oid_list | call_count | us_total | us_declare | us_children | us_self | time_stamp
---------------------------------------------+--------------------+------------+----------+------------+-------------+---------+-------------------------------
oid=0 inline_code_block | {0} | 1 | 24 | 19 | 0 | 24 | 2026-01-05 04:03:37.68941+00
oid=21721 complex_pkg.calculate_tax() | {21721} | 1 | 1309 | 8 | 815 | 494 | 2026-01-05 04:03:37.689697+00
oid=21721 complex_pkg.calculate_tax() +| {21721,4294957297} | 1 | 815 | 9 | 0 | 815 | 2026-01-05 04:03:37.690177+00
oid=4294957297 -> public.get_tax_rate() | | | | | | |
(3 rows)查看指定函数的行级详情
当定位到目标函数后,使用plsql_profiler_show_function()可以查看该函数内部每一行代码的详细性能数据。
-- 首先,从函数列表中获取目标函数的OID数组(func_oid_list)
-- 然后,将该OID数组作为参数传入以下函数
SELECT * FROM plsql_profiler_show_function(ARRAY['...']); -- 请将'...'替换为函数OID列表示例
SELECT * FROM plsql_profiler_show_function(ARRAY[21721,4294957297]);返回结果如下:
line_number | exec_count | total_time | percent | avg_time | min_time | max_time | source_code | time_stamp
-------------+------------+------------+---------+----------+----------+----------+-------------------------------------------+------------
0 | 1 | 351 | 100 | 351 | 351 | 351 | ---- Function Totals ---- |
1 | 0 | 0 | 0 | 0 | 0 | 0 | v_rate NUMBER; |
2 | 1 | 340 | 96.87 | 340 | 340 | 340 | BEGIN |
3 | 1 | 338 | 96.3 | 338 | 338 | 338 | IF p_income <= 50000 THEN |
4 | 0 | 0 | 0 | 0 | 0 | 0 | v_rate := 0.10; |
5 | 0 | 0 | 0 | 0 | 0 | 0 | ELSIF p_income <= 100000 THEN |
6 | 1 | 24 | 6.84 | 24 | 24 | 24 | v_rate := 0.20; |
7 | 0 | 0 | 0 | 0 | 0 | 0 | ELSE |
8 | 0 | 0 | 0 | 0 | 0 | 0 | v_rate := 0.30; |
9 | 0 | 0 | 0 | 0 | 0 | 0 | END IF; |
10 | 1 | 0 | 0 | 0 | 0 | 0 | RETURN v_rate; |
11 | 0 | 0 | 0 | 0 | 0 | 0 | END get_tax_rate; | 查看函数的行级性能数据
使用plsql_profiler_show_funclist()和plsql_profiler_show_function()函数可以联合查询所有函数内部每一行代码的详细性能数据。
SELECT * FROM plsql_profiler_show_funclist() F
CROSS JOIN LATERAL plsql_profiler_show_function(F.func_oid_list) C;步骤六:生成并查看HTML报告
为了更直观地分析,您可以使用plsql_profiler_get_report()生成一份完整的HTML格式报告。
-- 在psql客户端中,使用\o命令将输出重定向到文件
\o plsql_profiler_report.html
-- 开启仅显示元组模式,以便输出纯净的HTML内容
\t
-- 生成报告
SELECT plsql_profiler_get_report('My Performance Report');
-- 关闭重定向
\o
-- 关闭仅显示元组模式
\t 执行完成后,将返回一个包含完整报告内容的HTML字符串。请将该字符串复制到HTML文件中,并在浏览器中打开,以便查看详尽的性能分析报告。
步骤七:清理资源
分析工作完成后,执行以下操作来重置数据、禁用分析器并移除插件,以避免对后续操作产生性能影响。
-- 1. (可选)重置已采集的分析数据
SELECT plsql_profiler_reset();
-- 2. 禁用分析器
SELECT plsql_profiler_disable();
-- 3. (可选)如果不再需要,可以删除插件
DROP EXTENSION polar_plsql_profiler;相关函数参考
下表列出了POLAR_PLSQL_PROFILER插件提供的核心函数。
控制函数
plsql_profiler_enable()
说明:在当前会话中启用性能分析器。
语法:
sys.plsql_profiler_enable() RETURNS bool返回值:
bool类型。启用成功返回true,否则返回false。
plsql_profiler_disable()
说明:在当前会话中禁用性能分析器。
语法:
sys.plsql_profiler_disable() RETURNS bool返回值:
bool类型。禁用成功返回true,否则返回false。
plsql_profiler_reset()
说明:清除当前会话中已收集的所有性能分析数据。
语法:
sys.plsql_profiler_reset() RETURNS void返回值:无。
报告与分析函数
plsql_profiler_show_funclist()
说明:显示所有被调用函数的调用链和总体性能摘要,不包含分析器自身的函数。
语法:
sys.plsql_profiler_show_funclist() RETURNS TABLE(...)返回值:返回一个表,包含如下字段。
字段
说明
func_call函数调用链(文本)。
func_oid_list调用栈中的函数OID列表。
call_count函数被调用的次数。
us_total总执行时间(微秒)。
us_declare声明部分的执行时间(微秒)。
us_children子函数的执行时间(微秒)。
us_self函数自身的执行时间(微秒)。
time_stamp函数调用的时间戳。
plsql_profiler_show_function()
说明:显示指定函数内每一行代码的详细性能统计。
语法:
sys.plsql_profiler_show_function(func_oid OID[]) RETURNS TABLE(...)参数说明:
参数
说明
func_oid要显示详细信息的函数OID数组,可从
plsql_profiler_show_funclist()的func_oid_list列获取。返回值:返回一个表,包含如下字段。
字段
说明
line_number源代码中的行号。
exec_count此行被执行的次数。
total_time总执行时间(微秒)。
percent此行在函数总时间中所占百分比。
avg_time每次执行的平均时间(微秒)。
min_time此行的最小执行时间(微秒)。
max_time此行的最大执行时间(微秒)。
source_code此行的源代码。
time_stamp执行匿名块的时间戳,对于非匿名块为
null。
plsql_profiler_get_report()
说明:生成一份完整的HTML格式性能分析报告。
语法:
sys.plsql_profiler_get_report(IN title text DEFAULT 'PL/SQL PROFILER REPORT') RETURNS text参数说明:
参数
说明
title报告的标题,可选参数,默认为
PL/SQL PROFILER REPORT。返回值:
text类型。返回包含完整报告内容的HTML字符串。
底层数据获取函数
plsql_profiler_linestats_local()
说明:获取原始的行级统计信息,包括函数执行次数和计时详情。
语法:
sys.plsql_profiler_linestats_local() RETURNS SETOF record返回值:返回一个记录集,包含如下字段。
字段
说明
func_oid函数OID数组,表示调用堆栈。
line_number源代码中的行号。
exec_count此行被执行的次数。
total_time总执行时间(微秒)。
percent此行在函数总时间中所占百分比。
min_time此行的最小执行时间(微秒)。
max_time此行的最大执行时间(微秒)。
source此行的源代码。
time_stamp匿名块的时间戳。
plsql_profiler_callgraph_local()
说明:获取原始的函数调用图信息,包括函数调用次数和计时。
语法:
sys.plsql_profiler_callgraph_local() RETURNS SETOF record返回值:返回一个记录集,包含如下字段。
字段
说明
stack函数OID数组,表示调用堆栈。
call_count此函数被调用的次数。
us_total总执行时间(微秒)。
us_declare在声明部分花费的时间(微秒)。
us_children在子函数中花费的时间(微秒)。
us_self在此函数本身中花费的时间(微秒)。
time_stamp函数调用的时间戳。
plsql_profiler_func_oids_local()
说明:获取所有被分析过的函数的OID数组。
语法:
sys.plsql_profiler_func_oids_local() RETURNS oid[]返回值:
oid[]类型。返回一个包含所有被采集过信息的函数OID的数组。
plsql_profiler_funcs_source()
说明:根据函数OID数组返回其源代码。
语法:
sys.plsql_profiler_funcs_source(func_oids oid[]) RETURNS SETOF record参数说明:
参数
说明
func_oid要获取源代码的函数OID数组,可从
plsql_profiler_linestats_local()的func_oid列获取。返回值:返回一个记录集,包含如下字段。
字段
说明
func_oid函数OID。
line_number源代码中的行号。
source此行的源代码。
plsql_profiler_get_stack()
说明:将函数OID堆栈转换为函数签名数组。
语法:
sys.plsql_profiler_get_stack(stack oid[]) RETURNS text[]参数说明:
参数
说明
stack函数OID数组,表示调用堆栈。可从
plsql_profiler_callgraph_local()的stack列获取。返回值:
text[]类型。返回一个包含函数签名的文本数组。
plsql_profiler_get_stack_formatted()
说明:将函数OID堆栈转换为格式化的调用链和OID列表。
语法:
sys.plsql_profiler_get_stack_formatted(stack oid[]) RETURNS text[]参数说明:
参数
说明
stack函数OID数组,表示调用堆栈。可从
plsql_profiler_callgraph_local()的stack列获取。返回值:
text[]类型。返回包含格式化函数调用链和OID列表的文本数组。