polar_plsql_profiler

更新时间:
复制为 MD 格式

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及以上。

说明

您可在控制台查看内核小版本号,也可以通过SHOW polardb_version;语句查看。如未满足内核小版本要求,请升级内核小版本

优势

  • 代码行级分析:能够精确到每一行源代码的执行次数和耗时,快速定位热点代码。

  • 调用图谱:清晰展示函数间的调用关系,帮助理解整体耗时在不同函数模块间的分布。

  • 可视化报告:支持一键生成内容详尽的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列表的文本数组。

常见问题

为什么执行分析函数后看不到任何数据?

请检查以下几点:

  1. 您是否在执行目标PL/SQL代码之前,于同一个会话中执行了SELECT plsql_profiler_enable();。分析器只对启用后、在当前会话中执行的代码生效。

  2. 您的数据库会话是否中断重连过?分析数据是会话级别的,断连后会丢失。

可以在生产环境中使用这个工具吗?

不建议在生产环境中持续开启。分析器会带来一定的性能开销,仅适用于临时的性能诊断和调优。完成分析后,请务必禁用它。