Introduction
Viteza este un factor important
intr-o aplicatie. Abilitatea de a gasi inregistrarea sau setul de inregistrari
de care ai nevoie afecteaza direct manevrabilitatea aplicatiei tale. Intr-o
aplicatie unde tu lucrezi doar cu cateva zeci de mii de inregistrari, nu
trebuie neaparat sa te gandesti la optimizarea aplicatiei, dar cand incepi a
lucra cu cateva sute de mii de inregistrari sau chiar peste un milion, fara sa
optimizezi aplicatia, utilizatorii se for astepta ca aplicatia ta sa gaseasca
inregistrarile cerute de ei. In mod ciudat, utilizatorii au tendinta sa
gandeasca pentru un oarecare motiv ca timpul lor este foarte imortant, si
faptul ca aplicatia ta ii face sa astepte pana ce se gaseste inregistrarea sau
setul de inregistrari, ii enerveaza. Scopul acestui articol este acela de ati
arata cum sa folosesti optimizarea Rushmore ca sa iti faciliteze aplicatii mai
rapide. Cheia utilizarii Rushmore este sa intelegeti cum functioneaza. Aceasta
va faciliteaza incontinuare un design inteligent care va asigura o folosire
corecta a tehnologiei Rushmore.
„Sa privim la date”
Inainte sa incepem a ne uita la Rushmore, este foarte
important sa stim cate ceva despre cum se stocheaza inregistrarile in tabele de
pe discul fizic. Daca ar fi sa ne uitam la o tabela de pe discul tau, nu am
vedea coloane si randuri; in schimb am vedea un lung sir de date (un string
foarte lung). Prima ‚gramada’ de octeti ar contine capul de tabel (table
headder), daca ai avea rabdare sa descifrezi capul de tabel ai fi capabil sa
iti dai seama lungimea inregistrarii. Hai sa presupunem ca lungimea
inregistrarii tale are 500 de octeti. Daca ai vrea sa te uiti la urmatorea
inregistrare din tabela, ai avea nevoie sa muti capul de citire pe disc exact
500 de octeti de la sfarsitul capului de tabel. Ca sa ne uitam la inregistrarea
numarul 100 am avea deci nevoie sa citim exact 50 000 octeti de la sfarsitul
capului de tabel, s.a.m.d. Din nou daca ne-am uita la tabelul nostru de pe
disc, am constata ca inregistrarile din tabela nu sunt sortate dupa cheia
primara. VFP adauga inregistrari adaugandule la sfarsitul tabelei. Prima
inregistrare in tabela ar fi prima inregistrare adaugata iar a doua
inregistrare ar fi inregistrarea urmatore; altfel spus este foarte posibil ca
prima inregistrare din tabela ta sa fie inregistrarea cu numarul de ordine 1
(ReccNo = 1) si ultima inregistrare sa fie inreg cu nr. de ordine 2 (ReccNo =
2). Daca asa stau lucrurile mutarea capului de citire de la ReccNo=1 la
ReccNo=2 inseamna ca trebuie sa citim intreaga lungime a tabelei. Daca aceasta
tabela este una mare sau foarte mare, mutarea capului de citire va lua foarte
mult timp. Sa subliniem faptul ca aici este vorba de un proces mecanic care
este foarte
incet (aduti aminte de scrierea mecanica pe discheta).
Sortarea inregistrarilor pe masura ce sunt adaugate ia de
departe foarte mult timp. Daca ai vrea ca inregistrarile tale sa fie sortate
intr-o ordine, pe masura ce adaugi fiecare inregistrare, ai avea nevoie sa
folosesti o tabela temporara in care sa sortezi inregistrarile adaugate sau
modificate. Ai incepe prin a citi continutul tabelei existente si a compara cheia
inregistrarii noi cu cheia inregistrarii existente, daca cheia existenta este
mai mica decat cheia inregistrarii noi, ai salva acea inregistrare existenta in
tabela temporara si ai face loop in continuare spre inregistrarea urmatoare din
tabela originala. Ai continua acest loop pana cand ai gasi o inregistrare in
tabela existenta cu o cheie mai mare decat cheia inregistrarii pe care o adaugi.
Cand insfarsit ai gasi inregistrarea cu o cheie mai mare decat cheia noii
inregistrari, ar trebui sa salvezi noua inregistrare in tabela temporara dupa
cara sa salvezi inregistrarea curenta. Iarasi ar trebui sa faci loop prin toate
inregistrarile ramase in tabela originala si sa le salvezi in tabela temporara.
Acum stergi tabela existenta, redenumesti tabela temporara folosind numele
tabelei originale si ai avea o tabela sortata. Evident, toata operatiunea va
dura o gramada de timp si situatia in continuare se inrautateste – daca vei
adauga sute de inregistrari va trebui sa apelezi la aceasta procedura de
sortare pentru fiecare inregistrare nou adaugata. In versiunile mai vechi de
VFP exista comanda SORT care facea exact ce sa prezentat mai sus. Sortarea
inregistrarilor pe masura ce sunt adaugate pur si simplu ia foarte mult timp.
Din acest motiv foarte simplu VFP adauga inregistrarile la sfarsitul tabelei.
Crearea unei cereri rapide in VFP
Inainte sa ne uitam la Rushmore sa
ne uitam mai intai la cum functioneaza o cerere (query) fara Rushmore. Sa luam
aminte la urmatoarea cerere:
SELECT * ;
FROM MyTable ;
WHERE
cSomeField = lcSomeValue ;
INTO CURSOR csrMyResults
Din cauza ca datele din tabela nu sunt sortate in ordinea
selectarii inregistrarilor, pentru a satisface clauza WHERE, VFP trebuie mai
intai sa se uite la fiecare inregistrare in parte. Inregistrarile pot fi
adaugate in orice ordine, astfel incat nu exista nici o logica care sa ii spuna
lui VFP daca urmatoarea inregistrare ar putea contine o inregistrare care sa
contina criteriul cautat in clauza WHERE. Singura posibilitate fiind astfel de
a citi inregistrarea. Intradevar, VFP trebuie sa citeasca toate inregistrarile
din tabela deoarece oricare din inregistrari ar putea intruni criteriul clauzei
WHERE. Este important de subliniat ca atunci cand avem o interfata VFP,
conditia clauzei WHERE este evaluata de catre client (WORKSTATION) - nu de
server; „Ce inseamna asta?”, ca VFP sa evalueze o conditie WHERE, fiecare
inregistrare trebuie sa treaca prin retea la client. Daca lucrezi cu o tabela
cu multe campuri si inregistrari, expedierea acestora prin retea ar lua foarte
mult timp.
Factori cheie:
1. VFP trebuie sa se uite la fiecare inregistrare in parte;
2. Fiecare inregistrare trebuie expediata prin retea.
Daca Rushmore trebuie sa otimizeze aceasta cerere, acestia sunt doi factori
cu care trebuie sa se confrunte. Rushmore nu se uita efectiv la tabela; in
schimb se uita la indecsi.
Indecsi
Daca noi ar trebui sa intelegem cum functioneaza Rushmore, este foarte
important sa intelegem cum sunt construiti indecsi. Haide sa creem un index pe
care Rushmore lar folosi in aceasta cerere:
INDEX ON cSomeField TAG MyTag
VFP creaza un index care contine o adresa de referinta la inregistrare si
campul de referinta – cSomeField. Daca ar trebui sa ne uitam la index, ar
trebui sa vedem cam asa ceva:
Address Reference
|
Field Reference
|
|
12345
|
Adams
|
|
32145
|
Brown
|
|
...
|
...
|
|
09814
|
Murphy
|
|
14356
|
Smith
|
Asa dupa cum se vede, indexul este sortat in ordinea campului de referinta.
Adresa de referinta se indreapta catre inregistrarea din tabela curenta.
Folosind indecsul, VFP stie ca pentru a gasi o inregistrare continand
inregistrarea „Murphy”, are nevoie sa se duca la adresa 09814 din tabela. Ar fi
de observat ca un index are 2 caracteristici care determina sa fie utilizat in
optimizarea unei cereri. In primul rand un index este mic in comparatie cu
tabela insasi. Hai sa presupunem ca TabelaMea.DBF contine 50 coloane pentru un
total de inregistrari de 500 octeti. Indexul contine un camp de tip intreg (Integer)
ca adresa de referinta cu marimea de 4 octeti si un camp de referinta de 20
octeti insumand in total 24 octeti. (Asta presupune ca acel cSomeField de mai
sus are o lungime de 20 octeti. Un intrg in VFP are o lungime de 4 octeti) In
acest exemplu, indexul are 4,8% din marimea tabelei. In general, mutand capul
de citire de la o inregistrare la alta in acest index o sa ia 4,8% din timpul
care ne-ar lua ca sa mutam capul de citire de la o inregistrare la alta in
tabela noastra. Folosind acest index, VFP are nevoie sa mute capul de citire pe
o distanta mult mai mica.
Intradevar indexul sar putea sa fie destul de mic astfel incat sa incapa in
memorie. Daca VFP poate sa inglobeze indexul
in memorie, atunci va putea functiona mult mai rapid. Cand lucram cu
date pe disk, VFP trebuie sa mute fizic capul de citire pa discul fizic.
Din punct de vedere mecanic evident este mai incet decat daca am citi ceva
direct din memorie (cam asa functioneaza lucrurile si la noi ... mai repede
scoatem ceva din propria memorie decat atunci cand trebuie sa il citim de
undeva si apoi sa il reproducem).
In a doua parte indexul este sortat; asta inseamna ca exista o logica
aplicata pe care Rushmore sa o foloseasca atunci cand se uita la inregistrarea
urmatoare sa vada daca intruneste criteriul de cautare. Rushmore foloseste
algoritmul „B-TREE” in index pentru a determina daca o inregistrare intruneste conditiile
where. Rushmore incepe prin a se uita la mijlocul inregistrarii in index. Din
cauza ca acel index este sortat, poate pune intrebarea „Este aceasta referinta
pentru inregistrare egala, mai mare sau mai mica decat criteriul WHERE?” Daca
campul de referinta este mai mic decat criteriul WHERE, atunci Rushmore stie ca
urmatoarea inregistrare nu poate intruni criteriul WHERE. Daca referinta catre
inregistrare este mai mare decat criteriul WHERE atunci Rushmore stie ca
urmatoarea inregistrare ar putea intradevar intruni conditia lui WHERE. Cu alte
cuvinte prin citirea in mijlocul inregistrarii a unui index, Rushmore este in
stare sa elimine jumatate din inregistrari care nu corespund. Apoi Rushmore
face recursiv acelasi lucru cu inregistrarile ramase pana ajunge la criteriul
cautat. La a 2-a citire elimina 75% din inregistrari iar la a 3-a 87.5% din
inregistrari sunt eliminate, continua sa elimine pana ramane fara inregistrari
sau gaseste ce cauta. Daca Rushmore ramane fara inregistrari intoarce .F.
insemnand ca nu exista inregistrari potrivite criteriului de cautare. Daca
gaseste ce cauta la oricare citire, intoarce .T. insemnand bineinteles ca am
gasit ce cautam si astfel se opreste din citire. Intradevar, daca la prima
citire, Rushmore gaseste o referinta in index ca o potrivire exacta cu conditia
WHERE, intoarce .T. si cautarea se incheie.
Iti vei aduce aminte ca Rushmore are nevoie sa adreseze 2 factori – citirea
fiecarei inregistrari si expedierea acestora pe retea. Folosind un index, Rushmore
are nevoie numai sa trimita indexul prin retea. Trimite intregu index
bineinteles dar din cauza ca acesta este doar 4,8% din tabela avem mai putin de
trimis, in concluzie vom avea un transfer mult mai rapid. Folosind algoritmul „B-TREE” Rushmore are nevoie sa citeasca un procent foarte mic din
inregistrarile indexate. In improbabilitatea in care ai o tabela cu
130.000.000.000.000.000.000.000.000.000 de inregistrari, Rushmore trebuie sa
faca un maxim de doar 100 de citiri pentru a determina daca intruneste sau nu
criteriul cautat.
Unde este Rushmore folosit ?
Rushmore este folosit in optimizarea unor serii de comenzi inclusiv Select,
Average, Count, Sum, Browse, Copy To, Replace For etc. (Cauta in help pentru
lista completa). In marea majoritate a acestor comenzi Rushmore este obusnuit
sa aplice un filtru in clauza FOR. In clauza SELECT, el poate fi folosit sa
aplice un filtru si deasemenea poate fi folosit intr-o clauza JOIN. Luati seama
de urmatoarele 2 cereri:
SELECT Table1.SomeField, Table2.SomeOtherField ;
FROM Table1 ;
INNER JOIN Table2 ON Table1.FK = Table2.PK ;
WHERE Table1.nSomeField = lnSomeField ;
INTO CURSOR csrResults
SELECT Table1.SomeField, Table2.SomeOtherField ;
FROM Table1, Table2 ;
WHERE Table1.FK = Table2.PK AND ;
Table1.nSomeField = lnSomeField ;
INTO CURSOR csrResults
Ambele cereri intorc acelasi rezultat; dupa cum se poate vedea o clauza
JOIN este tratata ca o sortare. Intr-o clauza JOIN Rushmore se duce cu un pas
inainte. Rushmore vrea doar sa faca loop prin tabela cea mare odata. Sa luam aminte ca odata ce
inregistrarea/setul de inregistrari este gasit, Rushmore se opreste din cautat.
Este probabil ca Rushmore sa gaseasca ce cauta intr-o tabela mica mai repede
decat intr-o alta mai mare.
Optimizarea cererilor
Devreme ce stim acum ca Rushmore
foloseste indecsii ca sa optimizeze cererea, ai nevoie sa te asiguri ca
indecsii corespunzatori sunt creati corect. Acest fapt ne duce in mod logic la
intrebarea „Ce indecsi poate Rushmore sa foloseasca?”. Ei bine poate sa foloseasca
un index atata timp cat indexul nu este filtrat. Putem crea indecsi CDX structurali/nestructurali sau chiar indecsi
simpli IDX atata vreme cat acesti indecsi sunt actualizati si activi, Rushmore
ii va folosi. Spre exemplu:
INDEX ON MyTable.SomeField TO "Data\MyIndex.IDX"
INDEX ON SomeField TAG MyStructuralTag
INDEX ON SomeField TAG MyNonStructuralTag "Data\MyCompoundIndex.CDX"
SET INDEX TO "Data\MyIndex.IDX"
SET INDEX TO TAG MyStructuralTag ADDITIVE
SET INDEX TO TAG MyNonStructuralTag OF "Data\MyCompoundIndex.CDX" ADDITIVE
Nu numai ca Rushmore va folosi acest tip de index, ci va folosi si indecsi
multipli. Nu este nevoie sa creezi un index complex peste mai multe campuri. In
loc, poti sa creezi o serie de indecsi simpli peste campuri simple:
*** Instead of
INDEX ON cField1+TRANSFORM(nField2) TAG ComplexTag
*** These are better
INDEX ON cField1 TAG Field1Tag
INDEX ON nField2 TAG Field2Tag
O serie de indecsi simpli pot fi folositi de un numar de cereri diferite.
Acel index complex poate fi folosit intr-un singur scop. Rushmore NU POATE
folosi indecsi filtrati. Daca indexul tau are o clauza FOR, Rushmore o va ignora.
Cat costa adaugarea indecsilor
Foarte usor! Creaza un index peste fiecare camp in tabela ta, nu include o
clauza FOR, si astfel cererile vor fi intotdeauna optimizate. Din pacate este
si un cost asociat cu crearea indecsilor. Indecsii sunt SORTATI. Asta inseamna,
atunci cand adaugi o inregistrare in tabela, sau cand modifici un camp peste
care ai un index, trebuie sa actualizezi indexul. Sa ne aducem aminte ca
procesul de sortare a inregistrarilor intr-o tabela, este similar cu procesul
de actualizare al indecsilor. Cu cat mai multi indecsi ai cu atat va dura mai
mult sa ii actualizezi. Adaugarea indecsilor la toate campurile tale este in
definitiv o metoda proasta. Adauga
asadar numai indecsii de care STII ca ai nevoie. Definirea corespunzatoare a
aplicatiei inainte sa incepi sa codezi va asigura ca stii care indecsi sunt
aceia de care ai nevoie.
Rushmore si inregistrarile sterse
Cand executi comanda SET DELETED ON, VFP automat adauga echivalentul lui
„WHERE NOT DELETED()” sau a lui „FOR NOT DELETED()” ca si clauze la comanda ta
sau la cerere. Ca si cu alte conditii de filtrare, Rushmore poate folosi un
index ca sa optimizeze acesta conditie „WHERE NOT DELETED()”. Dupa cum sa
discutat in orice caz, este un cost asociat cu fiecare index. Daca ai numai
cateva inregistrari sterse in tabela ta, optimizarea acestei conditii in mod
foarte probabil nu va afecta in mod semnificativ viteza cererii tale. Pe de
alta parte, un index peste DELETED() va trebui sa fie actualizat pentru fiecare
inregistrare noua si de fiecare data cand vei sterge o inregistrare. Decizia de
a adauga un index DELETED() va trebui
luata in functie de fiecare situatie in parte. Daca decizi sa adaugi un index
DELETED(), asigurate ca este unul de tip binar:
INDEX ON DELETED() TAG DelTag BINARY
Asta cel putin va minimiza marimea indexului. In acest index vei avea
probabil o adresa pe 4 octeti si un camp bit – referinta pentru un total de 33
octeti (8 bit = 1 octet[byte])
Optimizare totala sau partiala
Sa ne uitam la SYS(3054) in help. Acesta funcie minunata iti va da un
raport de cum si cat de bine poate Rushmore sa iti optimizeze cererea. Dute in fereastra
de comenzi si da urmatoarele comenzi folosind SYS(3054):
SYS(3054,12) && Turn on SYS(3054)
SELECT ... INTO CURSOR MyCursor && Run your query
SYS(3054,0) && Turn SYS(3054) back off.
Cand sa nu folosesti Rushmore
Pentru marea majoritate Rushmore va face intotdeauna ca cererile tale sa mearga
mai repede. Sunt deasemenea si situatii cand Rushmore va incetini tot procesul
de optimizare. Sa ne aducem aminte ca Rushmore se uita sa vada care tabela este
mai mare ca sa faca un loop prin aceasta o singura data. Intr-o cerere complexa
care foloseste sub-cereri si cursoare derivate, Rushmore uneori intelege gresit
acest lucru; sfarsesti prin a cicla prin cursorul derivat odata si prin
cursorul mare de mai multe ori. Cand te afli in aceasta situatie poti folosi
clauza FORCE in declaratia SELECT pentru a te asigura ca sub-cererile sunt
evaluate in ordinea in care apar, sau poti sparge cererea intr-un numar de
cereri mai mici unde fiecare din aceste cereri este optimizat complet. O cerere
finala peste cursoarele rezultante serveste in a aduna rezultatele cursoarelor
mici si nu este optimizabila, dar prin aceasta, lucram doar cu un numar
restrans de inregistrari deci optimizarea cererii finale nu prea conteaza.
Concluzie
Pentru aceia dintre voi care nu au incercat inca sa foloseasca Rushmore,
veti descoperi o mica comoara; aplicatiile voastre vor rula mult mai usor. In "The Lord of the Rings" Gandalf il roaga
pe Shadowfax “Aratane ce inseamna viteza adevarata.”, il poti ruga pe Rushmore
sa faca la fel.
Spor
la optimizat
Textul original apartine dn-lui KEN MURPHY si a fost
publicat aici :
http://www.foxite.com/articles/read.aspx?id=79&document=making-Rushmore-rush-more
Tradus de Badita
Alexandru Stefan – RO
J4prog
[a] gmail [d] com