×
技术社区 >  技术博客 >  为什么你的 PL 调优没效果?先搞定慢 SQL 定位

为什么你的 PL 调优没效果?先搞定慢 SQL 定位

OceanBase 数据库中Oracle 租户PLSQL 调优的关键第一步,就是找到藏在程序包、存储过程里的慢 SQL。尤其是遇到嵌套调用的场景,手动排查堪比大海捞针。今天分享2个实战方法,帮你快速发现问题 SQL,为后续调优节省时间!

一、DBMS_PROFILER

逐行追踪,锁定存过内慢 SQL 位置

DBMS_PROFILER 是 Oracle 自带的性能剖析工具,能记录 PLSQL 每一行代码的执行详情,并将结果汇总起来,详细展示 PL 执行过程中每一条语句的执行时间。直接定位慢 SQL 在存储过程中的具体行号。

例如当我们需要分析当前这个存过中哪里消耗的时间较多导致整个存过执行时间变慢。

DECLARE 
	v_1 VARCHAR2(100);
	...
	v_3 VARCHAR2(100);
  BEGIN 
  CHS_TOA_TASK_PACKAGE.IS_HAS_MATCHED(?,?,v_1,v_2,v_3);
  END;

可以通过如下步骤:

1. 启动性能剖析器,备注任务名方便后续查询
CALL dbms_profiler.start_profiler(run_comment => '任务名' || SYSDATE);
2. 执行存过
3. 执行完成后,停止性能剖析器
call DBMS_PROFILER.stop_profiler();
4. 查看本次剖析的 runid
select  * from PLSQL_PROFILER_RUNS WHERE run_comment='任务名';
5. 将任务的runid带入一下查看存过的时间消耗
select 
a.runid,
a.unit_number,
a.unit_type,
a.unit_name,
b.line#,
b.total_occur,
b.total_ttime/1000000 as total_time_ms,
b.min_time as min_time_ns,
b.max_time as max_time_ns
 from PLSQL_PROFILER_UNITS a,PLSQL_PROFILER_DATA b where a.runid=b.runid and a.unit_number=b.unit_number and a.runid=? order by total_time_ms desc fetch first 100 rows only;

如下图,unit_name表示调用的存过名称,line#表示在这个存过内的哪一行,total_occur(执行次数),total_time_ms(总耗时),可以通过这4个字段判断出存过执行到结束,那条sql执行耗时最大,从而定位。

二、gv$ob_sql_audit

会话级采集,快速筛选高消耗 SQL

操作步骤

1. 新建独立会话窗口,通过以下sql获取当前会话id,(避免采集无关 SQL):
select sys_context('userenv','sid') from dual;
2. 在当前会话执行存过
3. 通过会话id采集存过执行的性能消耗
select sql_id,to_char(substr(query_sql,1,80)),plan_id,round(avg(elapsed_time),2),sum(elapsed_time) sum ,count(*),PL_TRACE_ID from gv$ob_sql_audit where sid=? and user_name=? group by sql_id,plan_id, to_char(substr(query_sql,1,80)),PL_TRACE_ID order by sum;

参考结果如下,会详细列出所有sql的执行时间消耗,从而定位慢SQL,但缺点是无法知道sql在存过调用中的哪一位置。所以需要配合上面DBMS_PROFILER方式。

三、实战总结

1.想精准定位慢 SQL 在存过中的行号:直接用 DBMS_PROFILER;

2.想快速筛选会话内高消耗 SQL:先用 gv$ob_sql_audit 做初步排查;

3.嵌套程序包场景:两者结合使用,效率翻倍,后续着重分析问题sql。

这两个方法覆盖了OceanBase 数据库Oracle 租户 PL 调优中慢 SQL 定位的核心场景,代码可直接复制套用。如果你也遇到 PLSQL 执行慢的问题,或是有独到的优化经验,欢迎在评论区留言分享,下次见!

精选推荐