Recentemente, enfrentei um desafio com um código PL/SQL extremamente lento em um cliente. O desempenho estava prejudicando operações críticas, e eu precisava identificar rapidamente onde estava o gargalo. Para isso utilizei uma ferramenta poderosa chamada DBMS_TRACE
que pode nos ajudar a rastrear e identificar os pontos lentos no código PL/SQL.
1. Instalar o Pacote DBMS_TRACE
Você pode baixar o script completo para instalação do pacote DBMS_TRACE
aqui. A instalação básica envolve apenas a execução do @?/rdbms/admin/tracetab.sql
. No entanto, eu criei novos objetos para facilitar o processo, além de conceder permissões de execução de forma pública.
ora18c:XEPDB1\sys = OPEN > @dbasobrinho.com.br_PLSQL_TRACE_01-instalar.sql
ora18c:XEPDB1\sys = OPEN >
ora18c:XEPDB1\sys = OPEN >
ora18c:XEPDB1\sys = OPEN > set lines 300 pages 100 timing off echo off
@?/rdbms/admin/tracetab.sql
Table dropped.
Table dropped.
Sequence dropped.
Table created.
Comment created.
Table created.
Comment created.
Sequence created.
no rows selected
OWNER OBJECT_NAME OBJECT_TYPE
---------------- ------------------------------ ------------------------------
PUBLIC PLSQL_TRACE_EVENTS SYNONYM
SYS PLSQL_TRACE_EVENTS TABLE
SYS PLSQL_TRACE_RUNNUMBER SEQUENCE
PUBLIC PLSQL_TRACE_RUNS SYNONYM
SYS PLSQL_TRACE_RUNS TABLE
PUBLIC VW_PLSQL_TRACE_EVENTS SYNONYM
SYS VW_PLSQL_TRACE_EVENTS VIEW
7 rows selected.
ora18c:XEPDB1\sys = OPEN > select 'grant ' || privilege || ' on ' || owner || '.' || table_name || ' to '||GRANTEE ||
2 decode(grantable, 'YES', ' with admin option') || ';' xxxxxxxxxxx
3 from dba_tab_privs
4 where table_name in ('PLSQL_TRACE_EVENTS', 'DBMS_TRACE');
XXXXXXXXXXX
----------------------------------------------------------------------
grant EXECUTE on SYS.DBMS_TRACE to PUBLIC;
1 row selected.
ora18c:XEPDB1\sys = OPEN >
ora18c:XEPDB1\sys = OPEN >
ora18c:XEPDB1\sys = OPEN >
ora18c:XEPDB1\sys = OPEN > create or replace view SYS.vw_plsql_trace_events as
2 SELECT sou.owner OBJ_owner
3 ,sou.name OBJ_NAME
4 ,trc.EVENT_SEQ id
5 ,trc.EVENT_TIME DT_TIME
6 --LEAD(trc.EVENT_TIME) OVER (PARTITION BY sou.name ORDER BY trc.EVENT_SEQ) - trc.EVENT_TIME AS TIME_TAKEN
7 ,ROUND((CAST(LEAD(trc.EVENT_TIME) OVER (PARTITION BY sou.name ORDER BY trc.EVENT_SEQ) AS DATE) - CAST(trc.EVENT_TIME AS DATE)) * 24 * 60 * 60, 2) AS SEGUNDOS
8 ,(SUBSTR(sou.text,1,100)) COMMAND_LINE
9 FROM plsql_trace_events trc, dba_source sou
10 WHERE sou.owner = sou.owner
11 AND sou.name = sou.name
12 AND sou.owner = trc.event_unit_owner
13 AND sou.name = trc.event_unit
14 AND sou.line = trc.event_line
15 AND trc.runid = (select max(runid) from plsql_trace_runs)
16 AND trc.event_unit_owner <> 'SYS'
17 ORDER BY trc.EVENT_SEQ
18 /
View created.
ora18c:XEPDB1\sys = OPEN >
ora18c:XEPDB1\sys = OPEN > grant select on SYS.PLSQL_TRACE_EVENTS to public;
Grant succeeded.
ora18c:XEPDB1\sys = OPEN > grant select on SYS.PLSQL_TRACE_RUNS to public;
Grant succeeded.
ora18c:XEPDB1\sys = OPEN > grant select on SYS.VW_PLSQL_TRACE_EVENTS to public;
Grant succeeded.
ora18c:XEPDB1\sys = OPEN > create or replace public synonym PLSQL_TRACE_EVENTS for SYS.PLSQL_TRACE_EVENTS;
Synonym created.
ora18c:XEPDB1\sys = OPEN > create or replace public synonym PLSQL_TRACE_RUNS for SYS.PLSQL_TRACE_RUNS;
Synonym created.
ora18c:XEPDB1\sys = OPEN > create or replace public synonym VW_PLSQL_TRACE_EVENTS for SYS.VW_PLSQL_TRACE_EVENTS;
Synonym created.
ora18c:XEPDB1\sys = OPEN > grant EXECUTE on SYS.DBMS_TRACE to PUBLIC;
Grant succeeded.
ora18c:XEPDB1\sys = OPEN >
ora18c:XEPDB1\sys = OPEN > set echo off;
ora18c:XEPDB1\sys = OPEN >
Download Script
2. Criando Tabela e Dados para Teste
Para realizar um teste real, vamos criar uma tabela e popular com dados aleatórios:
sqlplus sys/manager@pdb1 as sysdba
ora18c:XEPDB1\sys = OPEN > @dbasobrinho.com.br_PLSQL_TRACE_02-tabela_teste.sql
ora18c:XEPDB1\sys = OPEN >
ora18c:XEPDB1\sys = OPEN > begin execute immediate 'drop table SH.TX'; exception when others then null; end;
2 /
PL/SQL procedure successfully completed.
Elapsed: 00:00:00.27
ora18c:XEPDB1\sys = OPEN > create table SH.TX (EMPNO PRIMARY KEY, ENAME, JOB, MGR, HIREDATE, SAL, COMM, DEPTNO)
2 as
3 SELECT level as EMPNO
4 ,trim(DBMS_RANDOM.string ('X', TRUNC (DBMS_RANDOM.value (3,10)))) as ENAME
5 ,trim(DBMS_RANDOM.string ('X', TRUNC (DBMS_RANDOM.value (5,9)))) as JOB
6 ,DBMS_RANDOM.value(1,ROWNUM) as MGR
7 ,SYSDATE - DBMS_RANDOM.value(0,366) as HIREDATE
8 ,round(DBMS_RANDOM.value(1200,50000),2) as SAL
9 ,trunc(DBMS_RANDOM.value(1,100)) as COMM
10 ,trunc(DBMS_RANDOM.value(1,50)) as DEPTNO
11 FROM dual
12 CONNECT BY level <= 300000;
Table created.
Elapsed: 00:00:32.86
ora18c:XEPDB1\sys = OPEN >
ora18c:XEPDB1\sys = OPEN > exec DBMS_STATS.GATHER_TABLE_STATS(ownname => 'SH',tabname => 'TX');
PL/SQL procedure successfully completed.
Elapsed: 00:00:00.50
Download Script
3. Criando Procedure Function para Teste
Vamos criar uma procedure e uma função para simular uma rotina comum:
ora18c:XEPDB1\sys = OPEN > @dbasobrinho.com.br_PLSQL_TRACE_03-procedure.sql
Grant succeeded.
Elapsed: 00:00:00.04
ora18c:XEPDB1\sys = OPEN >
ora18c:XEPDB1\sys = OPEN > CREATE OR REPLACE FUNCTION SH.FNC_PAUSA(p1 IN NUMBER) RETURN NUMBER AS
2 BEGIN
3 sys.DBMS_LOCK.SLEEP(p1);
4 RETURN 1;
5 END FNC_PAUSA;
6 /
Function created.
ora18c:XEPDB1\sys = OPEN >
ora18c:XEPDB1\sys = OPEN > CREATE OR REPLACE PROCEDURE SH.PRC_TX
2 IS
3 v_SUM NUMBER;
4 BEGIN
5 SELECT /*SQL##0001.1##DBASOBRINHO*/
6 SUM(T1.SAL + T2.SAL)
7 INTO v_SUM
8 FROM
9 (SELECT SUM(T.SAL) SAL
10 FROM SH.TX T
11 WHERE T.JOB LIKE '%MANAGER%'
12 ) T1,
13 (SELECT SUM(T.SAL) SAL
14 FROM SH.TX T
15 WHERE T.JOB LIKE '%CLERK%'
16 ) T2
17 where FNC_Pausa(TRUNC(DBMS_RANDOM.VALUE(0, 16)) ) = 1;
18 --/
19 SELECT /*SQL##0001.2##DBASOBRINHO*/
20 SUM(T.SAL)
21 INTO v_SUM
22 FROM SH.TX T
23 WHERE T.DEPTNO = 10
24 AND FNC_Pausa(TRUNC(DBMS_RANDOM.VALUE(0, 16)) ) = 1;
25 --/
26 FOR C1 in(SELECT /*SQL##0001.3##DBASOBRINHO*/
27 EMPNO, ENAME, SAL, ROWID RW
28 FROM SH.TX
29 WHERE ENAME LIKE 'A%'
30 AND ROWNUM < 30)
31 LOOP
32 update /*SQL##0001.4##DBASOBRINHO*/ SH.TX
33 set JOB = UPPER(JOB)
34 WHERE ROWID = C1.RW
35 AND FNC_Pausa(TRUNC(DBMS_RANDOM.VALUE(0, 3)) ) = 1;
36
37 END LOOP;
38 END PRC_TX;
39 /
Procedure created.
ora18c:XEPDB1\sys = OPEN > set timing on
ora18c:XEPDB1\sys = OPEN >
Download Script
4. Executando sem o PLSQL_TRACE
Antes de ativarmos o PLSQL_TRACE
, vamos executar a procedure para termos uma referência do tempo de execução sem qualquer rastreamento habilitado:
ora18c:XEPDB1\sys = OPEN >
set timing on
begin
SH.PRC_TX;
end;
/
PL/SQL procedure successfully completed.
Elapsed: 00:00:41.29
ora18c:XEPDB1\sys = OPEN >
5. Executando com o DBMS_TRACE.trace_all_lines
Vamos utilizar o DBMS_TRACE.trace_all_lines
para rastrear todas as linhas executadas no código PL/SQL. Essa abordagem é útil para obter uma visão detalhada de cada linha de código que está sendo executada, permitindo identificar pontos específicos onde o tempo de execução pode ser melhorado.
ora18c:XEPDB1\sys = OPEN >
set timing on
begin
dbms_trace.set_plsql_trace(dbms_trace.trace_all_lines);
SH.PRC_TX;
dbms_trace.clear_plsql_trace;
end;
/
PL/SQL procedure successfully completed.
Elapsed: 00:00:43.99
ora18c:XEPDB1\sys = OPEN >
Para visualizar os resultados do rastreamento, utilizamos a visão VW_PLSQL_TRACE_EVENTS
:
ora18c:XEPDB1\sys = OPEN >
set lines 700 pages 500
col OBJ_OWNER for a12;
col OBJ_NAME for a20;
col ID for 999999
col DT_TIME for a20;
col COMMAND_LINE for a100;
col SEGUNDOS for 999999
SELECT * FROM VW_PLSQL_TRACE_EVENTS order by id
/

6. Executando com o DBMS_TRACE.trace_all_calls
Agora vamos ativar o rastreamento de todas as chamadas de procedimentos e funções usando DBMS_TRACE.trace_all_calls
. Este método rastreia cada chamada de procedimento e função dentro do código PL/SQL.
ora18c:XEPDB1\sys = OPEN > set timing on
begin
DBMS_TRACE.set_plsql_trace(DBMS_TRACE.trace_all_calls);
SH.PRC_TX;
DBMS_TRACE.clear_plsql_trace;
end;
/
PL/SQL procedure successfully completed.
Elapsed: 00:00:42.52
ora18c:XEPDB1\sys = OPEN >
Para visualizar os resultados do rastreamento, utilizamos a visão VW_PLSQL_TRACE_EVENTS
:
set lines 700 pages 500
col OBJ_OWNER for a12;
col OBJ_NAME for a20;
col ID for 999999
col DT_TIME for a20;
col COMMAND_LINE for a100;
col SEGUNDOS for 999999
SELECT * FROM VW_PLSQL_TRACE_EVENTS order by id
/

7. Executando com o DBMS_TRACE.trace_all_sql
Finalmente, vamos ativar o rastreamento de todas as instruções SQL executadas dentro do bloco PL/SQL usando DBMS_TRACE.trace_all_sql
.
ora18c:XEPDB1\sys = OPEN > set timing on
begin
DBMS_TRACE.set_plsql_trace(DBMS_TRACE.trace_all_sql);
SH.PRC_TX;
DBMS_TRACE.clear_plsql_trace;
end;
/
PL/SQL procedure successfully completed.
Elapsed: 00:00:40.21
ora18c:XEPDB1\sys = OPEN >
Para visualizar os resultados do rastreamento, utilizamos a visão VW_PLSQL_TRACE_EVENTS
:
ora18c:XEPDB1\sys = OPEN >
set lines 700 pages 500
col OBJ_OWNER for a12;
col OBJ_NAME for a20;
col ID for 999999
col DT_TIME for a20;
col COMMAND_LINE for a100;
col SEGUNDOS for 999999
SELECT * FROM VW_PLSQL_TRACE_EVENTS order by id
/

Diferenças entre os Métodos de Rastreamento
DBMS_TRACE.trace_all_sql:
- Rastreia todas as instruções SQL executadas dentro do bloco PL/SQL.
- Útil para identificar consultas SQL específicas que podem estar causando lentidão.
DBMS_TRACE.trace_all_calls:
- Rastreia todas as chamadas de procedimentos e funções dentro do bloco PL/SQL.
- Útil para entender o fluxo de chamadas dentro do bloco PL/SQL e identificar procedimentos ou funções problemáticos.
DBMS_TRACE.trace_all_lines:
- Rastreia a execução linha a linha do bloco PL/SQL.
- Fornece o nível mais detalhado de rastreamento, útil para depuração detalhada de blocos PL/SQL complexos.
Rastrear a execução de procedimentos PL/SQL com o DBMS_TRACE
é essencial para identificar e solucionar problemas de desempenho em códigos complexos. Com as opções de rastreamento trace_all_sql
, trace_all_calls
e trace_all_lines
, você pode obter uma visão detalhada do comportamento do código, identificar gargalos e otimizar o desempenho de maneira eficaz.
O trace_all_sql
é útil para diagnosticar consultas SQL específicas, o trace_all_calls
ajuda a entender o fluxo de chamadas de procedimentos e funções, e o trace_all_lines
oferece um rastreamento mais granular, ideal para depuração detalhada.
🚬🍺 #DBAProblemático #GuinaNãoTinhaDó #SeReagirBúmViraPó #CaceteDeAgulha #ClienteSemNoção #AjudaODBA #DBAHerói #FitaLoka #MetaTomarSeuLugar #QueFita #TicTac09e40 #VidaBandida #OMetrôVaiPassar #1000Fita #ZePovinho #Dimas #1000Faces #9M #GuinaTaEmCana #SemMiséria #DinheiroNaMão #SeguroVaiCobrir #ChicoteEstrala 🍺🚬