Tracing + TKProf = detaljna analiza za optimizaciju nekog (SQL) procesa

Friday, 23.11.2007 – Dejan

Ako pred sobom imate neku sporu aplikaciju, tj. neki spor SQL upit, u većini slučajeva ćete prvo pogledati šta kaže EXPLAIN PLAN, a ako ni nakon toga ne uočite potencijalni problem, onda u pomoć dolazi trejsovanje (tracing) i TKProf.

Trejsovanje (tracing) označava prikupljanje informacija tokom izvršavanja nekog SQL upita, a u te informacije između ostalih spadaju: kompletan SQL upit, koji se analizira, vrijeme izvršavanja tog upita (Parse, Execute, Fetch), “wait events” i mnoge druge nama nebitne informacije. Sadržaj jedne trace datoteke je u nečitljivom tzv. “human non-readable” formatu.

Ovako izgleda sadržaj jedne trace datoteke:

PARSING IN CURSOR #1 len=46 dep=1 uid=45 oct=3 lid=45 tim=2979116519 hv=3999262762 ad='3e87af28' 
SELECT * FROM TEST_TRACING WHERE MOD_10_ID = 3 
END OF STMT 
PARSE #1:c=31250,e=25142,p=3,cr=70,cu=3,mis=1,r=0,dep=1,og=1,tim=2979116515BINDS #1:
WAIT #1: nam='rdbms ipc reply' ela= 326 from_process=20 timeout=2147483647 p3=0 obj#=22 tim=2979116961 
WAIT #1: nam='rdbms ipc reply' ela= 81 from_process=20 timeout=2147483647 p3=0 obj#=22 tim=2979117161 
WAIT #1: nam='enq: PS - contention' ela= 1490 name|mode=1347616774 instance=2 slave ID=0 obj#=22 tim=2979125792 
WAIT #1: nam='enq: PS - contention' ela= 1055 name|mode=1347616774 instance=2 slave ID=2 obj#=22 tim=2979127098 
WAIT #1: nam='PX Deq: Join ACK' ela= 666 sleeptime/senderid=268566528 passes=1 p3=0 obj#=22 tim=2979127911 
WAIT #1: nam='PX Deq: Join ACK' ela= 51 sleeptime/senderid=268566528 passes=2 p3=0 obj#=22 tim=2979127991

TKProf je alat, koji standardno dolazi uz Oracle instalaciju, a služi analizi trace datoteka, tj. prikazivanju sadržaja it trace datoteke u čitljivom tzv. “human readable” formatu.

Ovako izgleda sadržaj trace datoteke nakon analize pomoću TKProfa:

SELECT * FROM TEST_TRACING WHERE MOD_10_ID = 3 
call     count       cpu    elapsed       disk      query    current        rows 
------- ------  -------- ---------- ---------- ---------- ----------  ---------- 
Parse        6      0.01       0.02          0          8          1           0
Execute      6      0.04       1.01          0         18          0           0 
Fetch      606      0.09       0.58          0          0          0       60000 
------- ------  -------- ---------- ---------- ---------- ----------  ---------- 
total      618      0.15       1.62          0         26          1       60000 

Misses in library cache during parse: 6 

Optimizer mode: ALL_ROWS 

Parsing user id: 45  (neki_korisnik)   (recursive depth: 1) 

Rows     Execution Plan 
-------  --------------------------------------------------- 
      0  SELECT STATEMENT   MODE: ALL_ROWS 
      0   PX COORDINATOR 
      0    PX SEND (QC (RANDOM)) OF ':TQ10000' [:Q1000] 
      0     PX BLOCK (ITERATOR) [:Q1000] 
      0      TABLE ACCESS   MODE: ANALYZED (FULL) OF 'TEST_TRACING' 
                 (TABLE) [:Q1000]

Pokazaću na jednom primjeru, kako se koriste tracing i TKProf, a usput i objasniti koje informacije su najbitnije za analizu nekog SQL upita…

Za potrebe ovog primjera trebamo kreirati jednu tabelu i popuniti ju podacima:

create table test_tracing (  
  test_id number, 
  test_naziv varchar2(32), 
  mod_10_id number) 
parallel (degree 4); 

insert /*+ APPEND */ into test_tracing 
SELECT LEVEL, 'naziv_' || TO_CHAR(level), mod(level, 10) FROM dual
CONNECT BY LEVEL <= 100000; 

COMMIT;

Tracing ćemo aktivirati pomoću procedure START_TRACE u paketu DBMS_SUPPORT. Ukoliko nemate instaliran taj paket, onda ga instalirajte izvršavanjem SQL skripte dbmssupp.sql:

SQL> conn sys/pass as sysdba 
Connected. 
SQL> @?\rdbms\admin\dbmssupp.sql

Dodajte pravo korištenja ovog paketa potrebnom korisniku:

SQL> grant execute on dbms_support to neki_korisnik;

Ako ste se uvjerili da je DBMS_SUPPORT instaliran, izvršite slijedeći upit:

begin 
  sys.dbms_support.start_trace(waits => true, binds => true); 
  FOR rec IN (SELECT * FROM test_tracing WHERE mod_10_id = 3) 
   LOOP 
      NULL; 
   END LOOP;
  sys.dbms_support.stop_trace; 

end;

U ovom slučaju će čitava tabela biti “skenirana” (“full table scan”). U to se možemo uvjeriti, ukoliko TKProf alatom analiziramo sadržaj kreirane trace datoteke:
C:\>tkprof C:\oracle\admin\testDB\udump\testDB_ora_3004.trc test_tracing.txt
waits=yes sys=no explain=neki_korisnik/sifra@testDB

Otvorimo datoteku test_tracing.txt i pronađimo gorekorišteni upit:

SELECT * FROM TEST_TRACING WHERE MOD_10_ID = 3 
call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ---------- 
Parse        6      0.01       0.02          0          8          1           0 
Execute      6      0.04       1.01          0         18          0           0 
Fetch      606      0.09       0.58          0          0          0       60000 
------- ------  -------- ---------- ---------- ---------- ----------  ---------- 
total      618      0.15       1.62          0         26          1       60000 

Misses in library cache during parse: 6 
Optimizer mode: ALL_ROWS 

Parsing user id: 45  (neki_korisnik)   (recursive depth: 1) 

Rows     Execution Plan 
-------  --------------------------------------------------- 
      0  SELECT STATEMENT   MODE: ALL_ROWS 
      0   PX COORDINATOR 
      0    PX SEND (QC (RANDOM)) OF ':TQ10000' [:Q1000] 
      0     PX BLOCK (ITERATOR) [:Q1000] 
      0      TABLE ACCESS   MODE: ANALYZED (FULL) OF 'TEST_TRACING' 
                 (TABLE) [:Q1000]

Uočavamo da je nad tabelom primijenjen “full table scan”, a kao moguće rješenje se nameće kreiranje indexa nad korištenom kolonom u WHERE klauzuli (tzv. predikat kolona).

Dodajmo sad index nad kolonom mod_10_id, da bismo vidjeli razliku u izvršavanju tog SQL upita:

CREATE INDEX neki_korisnik.INDX_TEST_TRACING_MOD_ID 
ON neki_korisnik.TEST_TRACING(MOD_10_ID) 
LOGGING 
NOPARALLEL
COMPUTE STATISTICS 
ONLINE;

Pokrenimo isti upit opet:

begin  
  sys.dbms_support.start_trace(waits => true, binds => true); 
  FOR rec IN (SELECT * FROM test_tracing WHERE mod_10_id = 3) 
   LOOP     
    NULL;
   END LOOP; 
  sys.dbms_support.stop_trace; 
end;

Sad možemo ponovo analizirati kreiranu trace datoteku i uočiti razliku između upita sa “full table scan” i upita sa korištenjem indexa:

SELECT * FROM test_tracing WHERE mod_10_id = 3
call     count       cpu    elapsed       disk      query    current        rows 
------- ------  -------- ---------- ---------- ---------- ----------  ---------- 
Parse        2      0.00       0.00          0          0          0           0 
Execute      2      0.00       0.00          0          0          0           0 
Fetch        2      0.00       0.00          6          8          0           2 
------- ------  -------- ---------- ---------- ---------- ----------  ---------- 
total        6      0.00       0.01          6          8          0           2 

Misses in library cache during parse: 2 
Misses in library cache during execute: 2 

Optimizer mode: ALL_ROWS 

Parsing user id: 45  (neki_korisnik)   (recursive depth: 2) 

Rows     Row Source Operation 
-------  --------------------------------------------------- 
      1  SORT AGGREGATE (cr=4 pr=3 pw=0 time=4410 us) 
   2500   VIEW  (cr=4 pr=3 pw=0 time=8748 us) 
   2500    COUNT STOPKEY (cr=4 pr=3 pw=0 time=6245 us) 
   2500     INDEX RANGE SCAN INDX_TEST_TRACING_MOD_ID (cr=4 pr=3 pw=0 time=3741 us)(object id 88431) 

Rows     Execution Plan 
-------  --------------------------------------------------- 
      0  SELECT STATEMENT   MODE: HINT: ALL_ROWS 
      1   SORT (AGGREGATE) 
   2500    VIEW 
   2500     COUNT (STOPKEY) 
   2500      INDEX   MODE: ANALYZED (RANGE SCAN) OF 
                 'INDX_TEST_TRACING_MOD_ID' (INDEX)

Iz rezultata se vidi da je upit sa indexom daleko brži.

Ovo je najbanalniji primjer, ali princip korištenja trejsovanja i TKProfa možete koristiti i za kompletne aplikacije, a ne samo za pojedinačne SQL upite.

Post a Comment