Intro: N-am mai scris de ceva timp un articol tehnic si simteam nevoia sa fac asta. Sunt multe subiecte pe care as vrea sa le ating, sunt multe articole pe care le-am inceput si nu le-am mai terminat dar imi revin.
Incep cu un subiect interesant (in opinia mea) care sper sa va stimuleze sa investigati mai mult problema optimizarii unei pagini web.

A optimiza o pagina web: o afirmatie cu multe sensuri. Optimizare pentru motoare de cautare, optimizarea vitezei de incarcare, optimizarea layout-ului pentru o utilizare cat mai buna, etc.
In acest articol voi scrie despre optimizarea vitezei de incarcare a unei pagini.

Timp de incarcare

1. Anatomia unei pagini web

Inainte de toate trebuie sa discutam despre ce se intampla din momentul in care introducem un url in browser si pana cand pagina s-a terminat de incarcat.
Putem imparti actiunile care au loc in urmatoarele tipuri , in ordine cronologica:

  • Server side: performanta este data de cat de bine sunt implementati algoritmii care genereaza continutul paginii si de accesul la resurse externe (storage, incluzand baze de date, API-uri)
  • Transferul fisierelor catre client: performanta este data de viteza cu care fisierele ajung de pe server pe client, numarul de fisiere sau marimea lor
  • Afisarea paginii la client: performanta este data de modul in care este construita pagina (html / css / js)

Fiecare dintre aceste tipuri de actiuni are o importanta majora in perfomanta generala a site-ului si atunci cand vrem sa optimizam viteza de incarcare a unei pagini web trebuie sa adresam fiecare tip de actiune in parte.

2. Optimizarea server side

Asa cum spuneam si la punctul anterior, aici vorbim despre doua aspecte: performanta algoritmilor pe care ii implementam si accesul la resurse externe.
Performanta algoritmilor pe care ii implementam tine, in general, de tipul de aplicatie pe care o dezvoltam si nu exista prea multe sfaturi general valabile care pot fie date. Totusi, sunt cateva lucruri de care trebuie sa ne asiguram in orice tip de aplicatie dezvoltam.
Accesul la resurse externe se refera la accesul la baza de date, la storage-ul fizic al serverului sau la API-uri externe.

Aspecte la care trebuie sa fim atenti:

  • Redundanta la construirea structurilor de date: atunci cand proiectam o baza de date trebuie sa analizam si cererile care se vor face catre aceasta si, in functie de tipurile de date care vor fi foarte accesate, frecventa si modul de accesare, trebuie sa pastram inregistrari redundante.
    Exemplu: se da o structura de categorii, una de tag-uri si una de articole; desi relatia intre articole si categorii si tag-uri se salveaza in tabele de join (tabele ce pastreaza strict aceasta relatie) este de preferat sa salvam si in tabela de articol atat denumirile categoriilor cat si cele ale tag-urilor; astfel evitam, la afisare, 2 join-uri pe care le-am face
  • Lucru pe baza de date in mod delayed: o mare problema de performanta apare in momentul in care selectam date dintr-o tabela si ordonam informatiile dupa un camp care este modificat foarte des. In cele mai multe cazuri engine-ul de baza de date nu va putea sa modifice acel camp si sa reconstruiasca un index pe acesta in timp util, astfel incat performanta sa nu fie afectata.
    Lucrul in mod delayed poate fi facut in doua feluri, in functie de necesitati: fie folosim instructiuni sql specifice atunci cand facem un update / insert, cum ar fi DELAYED sau LOW_PRIORITY, fie construim o structura de date paralela care sa ne permita o performanta ridicata.
    Exemplu: avem nevoie sa afisam articolele ordonate dupa numarul de afisari iar campul care tine numarul de afisari este in tabela de articole si trebuie incrementat la fiecare afisare a unui articol. O solutie ar fi sa facem update cu keyword-ul DELAYED pe tabela de articole, astfel incrementarea numarului de afisari se va face numai in momentul in care tabela este libera.
    O alta solutie ar fi construirea unei alte tabele in care sa tinem doar afisarile asociate unui articol, in esenta putem insera doar id-ul unui articol. Acest insert, cu sau fara keyword-ul LOW_PRIORITY este foarte rapid. La fiecare 10 – 15 minute putem rula un script care sa goleasca aceasta tabela si sa faca update pe tabela de articole pentru a modifica numarul de afisari.
  • Caching, in mai multe feluri: in circuitul informatiei intre server si client este bine sa folosim si proceduri de caching pentru a mari viteza cu care sunt servite informatiile si pentru a reduce incarcarea pe server.
    – cache pe file system: sectiuni de pe paginile service catre client pot fi salvate pe server ca si fisiere si pot fi servite de catre serverul web direct de acolo; in functie de frecventa de schimbare a continutului alegeti un timp de rescriere a acestor pagini; un astfel de sistem se foloseste pentru sectiuni de pagina care nu se schimba prea des insa sunt accesate foarte des (ex: listari de categorii cu count de produse)
    – cache in memorie: stucturi intregi de date pot fi salvate in memorie si pot fi accesate de catre orice proces al serverului web; un astfel de sistem se poate folosi pentru caching de cautari (se salveaza rezultatele in memorie pentru un interval de timp) sau de comentarii.
    – cache logic la nivelul bazei de date: acest sistem se foloseste in cazul in care stocam in baza de date informatii brute si le afisam in forma procesata; ex: salvam afisarile unei pagini si vrem sa afisam si numarul de afisari pe fiecare tip de browser, in cazul acest facem o noua tabela in care salvam aceasta informatie deja procesata
  • Executie de procese in background: sunt actiuni care trebuie executate in mod frecvent pe o aplicatie, cum ar fi procesarile necesare pentru caching; toate acestea trebuie rulate in procese separate.
  • Folosim sau nu un framework: in cele mai multe cazuri un framework inseamna o munca mai simpla si mai rapida pentru programator dar, in acelasi timp, inseamna si un load pe server care nu poate fi ignorat; intotdeauna trebuie analizat daca acest load se justifica (beneficiul adus de folosirea acelui framework sa justifice cheltuielile cu hardware-ul pentru a sustine acel framework).

3. Optimizarea client side

Desi optimizarea client side este strans legata de actiuni server side prefer sa le analizam separat.

  • Incarcare JS-uri: fisierele javascript se impart, in aceasta situatie, in 2 feluri: framework-uri JS si fisiere cu functii proprii.
    Fisierele ce contin definitii de framework-uri JS este de preferat sa le incarcam direct de pe site-ul de unde sunt distribuite pentru ca astfel se cache-uiesc de catre browser mult mai bine si sunt service catre client fara a incarca serverul pe care este aplicatia. Ex: jQuery este de preferat sa-l incarcam de la http://code.jquery.com/jquery-1.4.2.js .
    Fisierele cu functii proprii trebuie tratate altfel. In primul rand trebuie impartite in: 1) fisier cu functii generale si 2) fisiere cu functii specifice pe sectiuni. Aceste fisiere trebuie incarcate selectiv in functie de sectiunea accesata.
    Dupa ce ajungem la o versiune finala a unui fisier JS trebuie sa-l trecem printr-o procedura de minificare.
  • Incarcare CSS: in ce priveste fiserele CSS situatia este similara cu cea de la fisierele JS
  • Incarcare imagini:
    – procesarea imaginilor: pe un site putem afisa o imagine in mai multe dimensiuni (in listare, intr-o galerie, in preview, in vizualizare, etc); este de preferat ca la uploadul imaginii sa generam toate dimensiunile de imagini de care avem nevoie
    – salvarea imaginilor: atunci cand nu dispunem de un CDN este de preferat sa salvam imaginile intr-o structura separata de serverul care serveste si aplicatie pentru a putea distribui load-ul generat de aceste request-uri; pentru serverul care va servi imaginile puteti folosi lighttpd sau nginx.
    – servirea imaginilor de layout: o parte din layout-ul site-ului consta in imagini si in general vorbim de imagini multe si mici; este de preferat sa salvam toate aceste imagini intr-un sprite; mai multe despre acest subiect puteti citi aici.

Articolul este deschis iar pentru orice completari la aceasta lista de optimizari va rog sa lasati un comentariu.
Sunt intotdeauna deschis si la alte metode de optimizare 🙂 .

6 comments
  • Tweets that mention Optimizarea unei pagini web | Daniel Buca -- Topsy.com
    Posted on 17 aprilie 2010 at 23:46

    […] This post was mentioned on Twitter by Daniel Buca. Daniel Buca said: "Optimizarea unei pagini web dpdv tehnic" – http://bit.ly/c9CRWj , un articol pentru luni dimineata 🙂 […]

    Reply
  • Andrei Rinea
    Posted on 18 aprilie 2010 at 13:14

    Excelent articolul si e bine ca in coltisorul nostru de lume cineva se gandeste si la optimizari. As face niste mici comentarii si completari pe marginea acestuia.

    1. La punctul 2, redundanta aceasta, asa cum este ea descrisa, se numeste denormalizare in terminologiile RDBMS. In general este utila atunci cand normalizarea (procesul invers) este exagerat sau cand se foloseste un sistem de baze de date neperformant la join-uri (de ex. mysql)
    2. Citirile intensive pe tabele frecvent actualizate (cum e in cazul exemplului cu actualizarea numarului de afisari si sortarea in functie de aceasta la citire) se pot realiza in sisteme moderne folosind separatia izolarii tranzactiilor (SQL Server, Oracle etc.) intr-un mod perfect transparent (nu e necesar sa se modifice codul SQL existent). Chiar si in cazul mysql se poate folosi un engine mai orientat catre tranzactii (InnoDB de exemplu in loc de MyISAM) si astfel nu se va face lock pe toata tabela ci doar pe rand.
    3. „Cache logic la nivelul bazei de date” se numeste in terminologiile RDBMS agregari si tind spre cuburi OLAP asa cum sunt descrise. In cazul in care aplicatia este puternic orientata catre analize de date este recomandat sa se foloseasca un engine dedicat pentru asa ceva, scurtand timpul de dezvoltare, micsorand numarul de bug-uri si marindu-se performanta sistemului
    4. Un framework pentru aplicatia web poate introduce un cost de executie suplimentare sensibil doar in cazul mediilor interpretate fara opcode caching (ex php chior). Pentru mediile compilate (ASP.NET, Java etc.) costul suplimentar este de nivelul catorva nanosecunde (miliardimi de secunda) per thread = nesemnificativ. Insa chiar si mediile interpretate (Perl, php, python etc.) pot beneficia de un cache de opcode cum ar fi APC sau Zend Accelerator.

    Personal prefer introducerea unui cache / alte solutii de accelerare decat renuntarea la un framework de lucru. Asta pentru ca fara unul, dezvoltatorii vor crea un mare ghem de cod care pe masura ce creste ajunge de neintretinut si deseori asa apar rescrierile de aplicatii (situatie tare nasoala si costisitoare in viata unui proiect).

    As si niste mici completari privind incarcarea fisierelor la client :
    • Fisierele javascript ce fac parte din anumite framework-uri de client (jQuery etc.) pot fi incarcate si de pe CDN-uri ale unor companii mai mari (Microsoft, Google etc.) avand avantajul ca au datacenter-e in multe puncte din lume, asigurand, pe langa descarcarea serverelor proprii (asa cum bine ai mentionat), si apropierea geografica si comunicationala fata de client
    • Fisierele CSS au un tratament similar privind cele JS cu mentiunea celor JS proprii – i.e. nu prea poti (ieftin) sa iti gazduiesti pe un CDN fisiere CSS proprii. Poate poti gasi vreun CSS de resetare dar cam atat.

    In final mai adaug doar ca as fi considerat oportuna o mentiune ca anumite recomandari sunt specifice php/mysql (cum ar fi modul delayed / low_priority, costul de procesare in cazul folosirii unui framework etc) si faptul ca s-ar putea vorbi de ferme de server-e web si distributia incarcarii pe acestea in mod dinamic.

    Reply
  • Daniel Buca
    Posted on 18 aprilie 2010 at 15:31

    @Andrei Rinea
    Multumesc si eu pentru comentariu.
    Intr-adevar, articolul este orientat catre dezvoltatorii LAMP pentru ca in aceasta zona sunt si cei mai multi dezvoltatori dar si cele mai multe probleme.
    Constraint-urile pe care le impune .Net sau Java te ajuta sa eviti o parte din problemele descrise in articol.

    Legat de folosirea unui framework: depinde foarte mult si de proiectul pe care il dezvolti desi la high level projects este indiscutabila folosirea unuia.

    Voi updata si articolul cu cateva completari 🙂

    Nu in ultimul rand: ma bucur ca exista oameni receptivi si la astfel de articole 😉

    Reply
  • fabby
    Posted on 19 aprilie 2010 at 10:53

    Interesant articolul…dar nu tot, pentru ca sunt unele chestii care nu le inteleg. De recomandat ar fi si cel putin un exemplu pentru fiecare chestiune.
    Oricum, este de retinut.
    Merci!

    Reply
  • Daniel Buca
    Posted on 19 aprilie 2010 at 12:04

    @fabby
    Urmeaza un update al articolului cu cateva observatii de la Andrei si cateva completari.

    Reply
  • Andrei Rinea
    Posted on 19 aprilie 2010 at 16:24

    @fabby : Intreaba ce anume nu ai inteles si macar unul din noi iti poate da o explicatie/indrumare

    Reply

Leave a comment