Sunteți pe pagina 1din 9

Îmbunătăţirea performanţei SQL în ORACLE 10g utilizând

optimizatorul bazat pe cost


Optimizatorul bazat pe cost (CBO) este o componentă sofisticată care influenţează fiecare interogare care
este executata in Oracle 10g. Optimizatorul evaluează fiecare tip de SQL şi generează cel mai bun plan de
execuţie. Sintagma "cel mai bun plan de execuţie" poate depinde de la o bază de data la alta, în funcţie de
anumite criterii. Pentru o anumită bază de date un plan de execuţie bun se poate referi la returnarea
rezultatului interogarii într-un timp cât mai mic, pe când la o altă baze de date poate fi returnarea rezultatului
cu un consum cât mai mic de resurse. De obicei, Cel mai bun plan de execuţie urmăreşte o medie a celor
două.

Pentru a se obţine performanţa, trebuie să fie înţeleşi toţi factorii care influenţează optimizatorul: atât interni
cât şi externi. Cei mai importanţi factori sunt parametrii şi statisticile optimizatorului.

Parametrii optimizatorului bazat pe cost


CBO este influenţat de diferite setări de configurare. Aceste setări pot avea un impact important asupra
performanţei CBO-ului. Primul dintre acestea este optimizer-mode. Modurile de optimizare din Oracle 9i sunt
următoarele: rule, choose, all_rows şi first_rows. Cum primii doi erau speciali pentru optimizatorul bazat pe
reguli, în următoarele rânduri ne vom axa pe ultimii doi.

Optimizer_mode
Optimizatorul poate fi setat pentru toate sesiunile, pentru o singură sesiune, sau pentru un singur SQL.

Exemple :

alter system set optimizer_mode = first_rows_10;

alter session set optimizer_goal = all_rows;

select * /*+ first_rows(100) */ from student;

Modul all_rows este destinat pentru minimizarea resurselor şi favorizează scanările de tip full-table. În
schimb, modul first_rows realizează mai multe operaţiuni de tip I/O dar returnează mai repede liniile (primele
n dintre acestea).

Pentru a alege cel mai bun plan de execuţie într-o anumită situaţie se recurge la unul dintre modurile
optimizatorului.

Modul first_rows

Acest mod va întoarce rezultatul iniţial într-un timp cât mai scurt fără să ţină cont de resursele utilizate, şi
chiar cu posibilitatea ca rezultatul final să fie obţinut într-un timp mai mare. De obicei acest mod alege să
folosească un index pentru returnarea rezultatului. Acest mod este util atunci când utilizatorul are nevoie să
vadă o mică parte a rezultatului într-un timp cât mai scurt.

Modul all_rows
Acest mod va întoarce rezultatul încercând să minimizeze resursele folosite şi este favorabil atunci când nu
este nevoie de un rezultat parţial. Acest mod va prefera scanările de tip full-table şi nu cele care folosesc un
index. Trebuie evitat atunci când aplicaţia are nevoie de rezultate intermediare în timp real.

Modul first_rows_n

Acest mod are câteva opţiuni predefinite : first_rows_1, first_rows_10, first_rows_100, şi first_rows_1000.
Precizând dinainte câte rânduri se doresc a fi întoarse optimizatorul este ajutat să facă o decizie în privinţa
alegerii sau nu a unui index.

Modul rule

Acest mod este unul învechit şi nu este recomandat pentru folosire deoarece nu foloseşte noile îmbunătăţiri
care au apărut în Oracle după 1994 cum ar fi indexul de tip bitmap.

Deşi 'optimizer-mode' este cel mai important parametru pentru CBO, mai există şi alţi parametri care
influenţează comportamentul CBO-ului. Deşi valorile implicite ale acestora pot fi schimbate, Oracle nu
recomandă schimbarea lor decât când se cunoaşte foarte bine ceea ce implică acest lucru, pentru că
schimbarea valorilor poate influenţa radical planurile de execuţie.

Modul choose

Modul choose este cel implicit. Atunci când parametrul optimizer_mode este setat pe choose, atunci va fi ales
optimizatorul bazat pe cost când sunt disponibile statistici şi optimizatorul rule în caz contrar.

Optimizer_index_cost_adj
Acest parametru influenţează costul folosirii unui index. Cu cât valoarea este mai mică, cu atât este preferată
folosirea indecşilor.

Optimizer_index_caching
Acest parametru indică procentul probabil din index care se află în RAM. El afectează decizia CBO-ului în
privinţa alegerii folosirii indexului în join-uri în loc de scanări de tip full-table.

Db_file_multiblock_read_count
Când acest parametru are o valoare mare, atunci CBO-ul va decide că citirile de tip multiblock sunt mai puţin
costisitoare decât citirile secvenţiale. Acest lucru va favoriza scanările de tip full-table.

Hash_area_size
Acest parametru favorizează join-urile de tip hash faţă de cele de tip nested sau merge.

Sort_area_size
Acesta influenţează decizia dintre a alege între a folosi un index sau a sorta rezultatul. Cu cât este mai mare
valoarea, cu atât va fi preferată sortarea.

Operaţiile pe care le efectuează optimizatorul


Pentru fiecare interogare SQL procesată de Oracle, optimizatorul efectuează următoarele operaţii:

Evaluarea expresiilor şi condiţiilor


Optimizatorul evaluează mai întâi expresiile şi condiţiile care conţin constante.

Transformarea interogărilor
Pentru interogările complexe, care conţin subinterogări sau view-uri, optimizatorul transformă interogarea
iniţială într-o interogare echivalentă.

Alegerea scopului
Implicit, scopul optimizatorului este returnarea rezultatului final folosind cât mai puţine resurse. Scopul este în
strânsă legătură cu planul de execuţie. Aşa cum a mai fost precizat, scopul depinde de tipul aplicaţiei şi de
nevoile acesteia. Scopul poate fi stabilit prin setarea parametrului 'optimizer-mode', prin folosirea hint-urilor, şi
prin folosirea statisticilor.

Alegerea modului de acces


Modul de acces se referă la modul în care datele din tabele sunt accesate. În general, modul de acces prin
index este preferat când sunt returnate un număr mic de înregistrări iar accesul de tip full-table este preferat
când sunt accesate porţiuni mari din tabele. Lista modurilor de acces este următoarea:

 Full table scan


 Index Scan
 Rowid Scan
 Cluster Scan
 Hash Access
 Sample Table Scan

Alegerea ordinii tabelelor în join-uri


Atunci când o interograre are ca sursă mai multe tabele, Oracle întocmeşte o ordine, în funcţie de anumite
criterii. De exemplu, atunci când tabelele au constrângeri de tipul UNIQUE sau PRIMARY KEY, aceste tabele
vor fi primele. După ce a stabilit ordinea tabelelor, Oracle generează un set de planuri de execuţie, cărora le
ataşează costurile şi îl alege pe acela cu costul cel mai mic.

Folosirea hint-urilor
Hint-urile (indicii) necesită o mare atenţie atunci când se urmăreşte îmbunătăţirea performanţei unei
interogări. Atunci când Oracle nu are destule resurse pentru a alege un plan de execuţie cât mai aproape de
optim, se poate schimba 'manual' traiectoria acestui plan de execuţie prin sugerarea anumitor puncte cheie.
Folosirea lor schimbă dramatic planul de execuţie generat de Oracle, şi de aceea trebuie folosite numai ca o
ultimă soluţie, şi numai după anumite teste de performanţă care dovedesc că planul de execuţie este
influenţat în bine. Oracle permite folosirea a peste 120 de hint-uri, nu toate fiind documentate. O lista a celor
documentate este următoarea:
În afară de acestea, Oracle mai dispune şi de câteva hint-uri pentru care nu a pus la dispoziţie documentaţie.
Lista acestora este următoarea:
Hint-urile din listele de mai sus sunt grupate pe următoarele categorii :

 Hint-uri pentru Scop


 Hint-uri pentru Modul de acces
 Hint-uri pentru transformarea interogărilor
 Hint-uri pentru ordinea tabelelor în join
 Hint-uri pentru pentru join-uri
 Hint-uri pentru execuţia paralelă
 Hint-uri adiţionale

Trebuie precizat că uneori nu este de ajuns să fie specificat un singur hint pentru ca planul de execuţie să fie
influenţat. De exemplu, dacă a fost specificat să fie folosit un anumit index folosind un hint, dar modul de
acces nu a fost specificat, Oracle poate alege un mod de acces care să nu folosească un index şi atunci hint-
ul respectiv să fie ignorat. De aceea, se recomandă utilizarea mai multor hint-uri odată, care să ducă la bun
sfârşit alegerea planului de execuţie optim.

Manipularea statisticilor optimizatorului


Aşa cum a fost precizat, statisticile joacă un rol important în performanţa CBO-ului. Statisticile sunt colectate
şi folosite prin intermediul pachetului DBMS_STATS.

Statisticile optimizatorului sunt o colecţie de date care descriu detaliat informaţii despre baza de date şi
despre obiectele acelei baze de date. Aceste statistici, ca şi celelalte informaţii prezentate până acum sunt
folosite de optimizator pentru alegerea unui plan de execuţie optim pentru fiecare declaraţie SQL în parte.

Statisticile optimizatorului sunt următoarele:

 Statistici despre tabele


 Numărul de rânduri
 Numărul de blocuri (blocks)
 Media lungimii unui rând
 Statistici despre coloane
 Numărul de valori distincte dintr-o coloană
 Numărul de valori NULL dintr-o coloană
 Distribuţia datelor (histograma)
 Statistici despre indecşi (Oracle foloseşte în special arbori B-tree pentru stocarea indecşilor)
 Numărul de frunze
 Numărul de nivele
 Clustere
 Statistici legate de sistem
 Performanţa I/O
 Performanţa şi utilizarea procesorului

Statisticile optimizatorului sunt stocate în dicţionarul de date. Din cauză că obiectele dintr-o bază de date sunt
într-o continuă modificare, statisticile trebuie actualizate permanent. Ele sunt actualizate de Oracle automat
dar există şi opţiunea de a fi actualizate manual folosind pachetul DBMS_STATS. DBMS_STATS oferă de
asemenea, proceduri pentru a manipula statisticile colectate. Statisticile pot fi exportate de pe un sistem şi
importate pe un altul. De exemplu, pot fi exportate statisticile de pe producţie şi importate pe un sitem de test.
Ele pot fi şi blocate, în sensul că se poate specifica la un moment dat ca acestea să nu mai fie schimbate.

Colectarea automată a statisticilor


Oracle recomandă ca statisticile să fie colectate automat. Oracle colectează statisticile pentru toate obiectele
unei baze de date şi le întreţine actualizându-le permanent. Prin colectarea automată se elimină multe
procese ce trebuie executate pe rând manual şi se elimină posibilitatea de a obţine un plan de execuţie slab
din cauza lipsei statisticilor.

GATHER_STATS_JOB

Colectarea automată se realizează cu GATHER_STATS_JOB. Acest proces adună statistici pentru toate
obiectele care au statistici lipsă sau cele care există sunt vechi. Acest proces este creat automat atunci când
baza de date este creată şi este manipulat de Scheduler. Setarea de bază porneşte colectarea statisticilor în
fiecare noapte între 10 P.M. şi 6 A.M şi toată ziua în weekend.

Atributul stop_on_window_close se referă la fereastra pentru Scheduler şi cu ajutorul lui se setează dacă
GATHER_STATS_JOB să mai ruleze sau nu după ce fereastra Scheduler ului se închide. Valoarea standard
este TRUE. Obiectele pentru care statisticile nu au fost colectate vor rămâne pentru următoarea procesare.

Procesul GATHER_STATS_JOB colectează statisticile prin apelarea procedurii


DBMS_STATS.GATHER_DATABASE_STATS_JOB_PROC. Procedura colectează statisticile pentru
obiectele bazei de date pentru care nu s-au mai colectat până acum, iar dacă acestea există, ele sunt aduse
la zi, conform ultimelor modificări din baza de date. Actualizarea se face numai dacă modificările au fost
făcute pe cel putin 10% din înregistrări. Echivalentul acestei proceduri este
DBMS_STATS.GATHER_DATABASE_STATS cu opţiunea GATHER AUTO. Diferenţa dintre cele două este
că prima ţine cont de obiectele care necesită actualizarea mai mult decât celelalte şi le dă acestora prioritate.
Acest lucru ajută ca obiectele care necesită actualizarea statisticilor să fie procesate cu o mai mare
posibilitate înainte ca fereastra Scheduler ului să fie închisă. Aşa cum a fost precizat, implicit statisticile sunt
colectate automat. Dacă se doreşte ca acest lucru să fie verificat se poate vede prin :

SELECT * FROM DBA_SCHEDULER_JOBS WHERE JOB_NAME = 'GATHER_STATS_JOB';


Dacă din diferite motive se doreşte oprirea colectării statisticilor se poate face prin :

BEGIN
DBMS_SCHEDULER.DISABLE('GATHER_STATS_JOB');
END;
/

Există un parametru care se referă numai la statisticile învechite. Acest parametru se numeşte
STATISTICS_LEVEL. Statisticile învechite sunt actualizate dacă acest parametru este setat pe TYPICAL sau
pe ALL.

Colectarea manuală a statisticilor


Colectarea automată a statisticilor ar trebui să fie îndeajuns de mulţumitoare pentru majoritatea obiectelor
unei baze de date care este modificată cu o viteză medie. Cu toate acestea, există unele cazuri în care
colectarea automată să nu aibă rezultatele aşteptate. În general există 2 feluri de obiecte pentru care
colectarea manuală este recomandată: tabelele temporare care sunt des şterse sau truncate sau reconstruite
şi tabelele care sunt ţinta unor modificări masive mai ales în timpul zilei, având în vedere timpul în care
colectarea automată are loc (noaptea). Pentru tabelele temporare există două feluri de a rezolva această
problemă. Prima solutie este următoarea: colectarea statisticilor să nu mai fie efectuată pentru ele; în acest
caz, când optimizatorul întâlneşte o astfel de tabelă atunci în mod dinamic colectează statistici pentru
aceasta. O a doua soluţie este ca să se colecteze statistici pentru tabela respectivă într-un moment
reprezentativ pentru aceasta şi apoi să se blocheze aceste statistici. Pentru celălalt tip de obiecte, adică
pentru tabelele modificate foarte des şi foarte mult odată se recomandă colectarea statisticilor imediat după
ce inserarea sau modificarea a fost făcută, preferabil ca parte integrantă a modificării efectuate.

O altă situaţie în care colectarea manuală este recomandată este reprezentată de statisticile legate de
sistem, deoarece acestea nu sunt automat colectate. Acest lucru poate fi efectuat prin procedura
GATHER_FIXED_OBJECTS_STATS. Procedura trebuie apelată atunci când baza de date are o activitate
reprezentativă.

Reconstituirea vechilor statistici


Atunci când noi statistici sunt colectate cele vechi sunt salvate automat cu posibilitatea reconstituirii lor
utilizând procedura RESTORE din pachetul DBMS_STATS_JOB. Acest lucru este util atunci când noile
statistici colectate se dovedesc a avea o influenţă negativă asupra planului de execuţie. În view-urile
*_TAB_STATS_HISTORY (ALL, DBA, or USER) se poate vedea istoria modificărilor asupra statisticilor.
Valoarea pentru exprimarea timpului pentru care statisticile vechi sunt păstrate poate fi schimbată prin
procedura ALTER_STATS_HISTORY_RETENTION din pachetul DBMS_STATS. Valoarea implicită este 31
zile, adică se pot reconstitui statistici din ultimele 31 zile. GET_STATS_HISTORY_AVAILABILTY returnează
momentul in care au fost colectate cele mai vechi statistici păstrate. Statisticile mai vechi de acest moment nu
pot fi reconstituite. Procedura RESTORE nu poate reconstitui statisticile definite de utilizator. De reţinut atunci
când se doreşte golirea unei tabele este de preferat să fie folosit TRUNCATE în loc ca tabela să fie ştearsă şi
reconstituită, pentru că prin ştergere se şterg şi statisticile aferente tabelei respective.

Blocarea statisticilor
Atunci când se doreşte ca statisticile să nu fie schimbate din diferite motive, se poate apela la blocarea
statisticilor. Acestea nu mai pot fi schimbate până în momentul în care sunt deblocate. Pachetul
DBMS_STATS oferă câte două proceduri de blocare şi deblocare a acestora:

 LOCK_SCHEMA_STATS
 LOCK_TABLE_STATS
 UNLOCK_SCHEMA_STATS
 UNLOCK_TABLE_STATS

Exportarea şi importarea statisticilor


Aceste două procese permit crearea mai multor versiuni de statistici care pot fi folosite oricând dar cel mai
important aspect este reprezentat de faptul că pot fi exportate de la o bază de date la alta, de exemplu de la o
bază de date din producţie pe una de test. Aceste două procese nu trebuie confundate cu EXP şi IMP oferite
de pachetul DBMS_STATS care se referă la fişiere. În vederea exportării mai întâi se crează un tabel care să
conţină aceste statistici folosind procedura DBMS_STATS.CREATE_STAT_TABLE. După ce acest tabel este
creat exportarea propriu-zisă se realizează prin apelul procedurii DBMS_STATS.EXPORT_*_STATS.
Importarea se realizează prin apelul procedurii DBMS_STATS.IMPORT_*_STATS. Procesele de import-
export sunt asemănătoare cu reconstituirea statisticilor ca funcţionalitate. În general, se foloseşte
reconstituirea atunci când se doreşte restabilirea unor statistici mai vechi care nu au fost exportate, sau când
nu se doreşte exportarea manuală a statisticilor. În schimb, atunci când se doreşte mutarea statisticilor de pe
o bază de date pe alta sau când se doreşte păstrarea acestora pe o perioadă mai mare de timp, atunci se
apelează la import_export.

Costuri externe
Începând cu Oracle 9i şi continuând cu Oracle 10g, optimizatorul a fost schimbat astfel încât să ţină cont şi de
influenţe externe atunci când stabileşte planul de execuţie. În Oracle 10g costurile legate de procesor
influenţează implicit, comportamentul optimizatorului datorită legăturii directe dintre acesta şi execuţiile
fiecărei faze ale unui SQL, deşi este recomandat ca utilizatorii profesionişti să apeleze
DBMS_STATS.GET_SYSTEM_STATS pentru un management mai bun. În general costurile procesorului
(CPU_COST) nu influenţează, dar sunt cazuri în care instanţa de Oracle foloseşte excesiv resursele
procesorului. Un alt tip de cost extern este IO_COST care se referă la numărul de citiri şi scrieri fizice de
blocuri pe disk. IO_COST este proporţional cu numărul de citiri şi scrieri.

Concluzii
Cheia pentru folosirea adecvată a optimizatorului bazat pe cost constă în respectarea paşilor următori:

Reanalizarea statisticilor numai atunci când e nevoie. O greşeală frecventă a administratorilor de baze de
date este că analizează mai des decât este nevoie baza de date în vederea colectării statisticilor. Scopul
colectării este acela de a îmbunătăţi planul de execuţie. Dacă acesta nu este unul slab atunci nu este nevoie
de îmbunătăţire. Dacă performanţa curentă este satisfăcătoare, prin reanalizare este posibil să apară
probleme de performanţă.

Rescrierea SQL-urilor. De multe ori programatorii îşi asumă ideea că este deajuns să fie scrisă o interogare
care returnează rezultatul corect fără să ţină cont de performanţă. În realitate scrierea unei astfel de interogări
nu reprezintă nici jumătate din timpul necesar scrierii unei interogări corecte. Accesarea unei baze de date
într-un mod optim de multe ori se dovedeşte a fi un pas important dacă nu vital pentru succesul unei aplicaţii.

Importarea statisticilor de pe baza de date de producţie pe cea de test este iar un factor decisiv care asigură
că SQL-ul va avea acelaşi plan de execuţie pe ambele baze de date.

Schimbarea parametrilor CBO-ului trebuie pe cât posibil evitată pentru că este foarte periculoasă, mai ales
când nu se cunoaşte ceea ce implică acest lucru şi de multe ori schimbarea poate fi critică.

Uneori, rescrierea unui SQL ţinând cont de cele prezentate poate schimba atât de drastic timpul de execuţie,
încât de la câteva ore se poate ajunge la câteva secunde.

Bibliografie

1. Oracle Database Performance Tuning Guide, 10g Release 2 (10.2), Primary Author: Immanuel Chan
2. Inside the Oracle Cost Based Optimizer - Donald K. Burleson
3. The Oracle SQL Optimizers - Donald K. Burleson

S-ar putea să vă placă și