Svi članci
Web razvoj

Database optimizacija za brže učitavanje stranica

Database optimizacija za brže učitavanje stranica

Kada govorimo o brzini web stranice, većina razgovora se vrti oko optimizacije slika, minifikacije koda ili izbora hostinga. Sve su to legitimne teme, ali postoji jedan sloj koji ostaje nevidljiv sve dok ne počne stvarati probleme: baza podataka. Na dinamičnim stranicama, gdje se svaki prikaz sastavlja iz desetina ili stotina upita, baza je vrlo često stvarno usko grlo. Korisnik vidi učitavanje koje traje dvije ili tri sekunde, a uzrok nije server niti mreža nego upit koji prolazi kroz cijelu tabelu da bi pronašao jedan red. U ovom tekstu razlažemo kako baza utiče na percipiranu brzinu i koje tehnike daju mjerljive rezultate.

Zašto je baza podataka često pravi krivac za sporu stranicu

Svaki put kada se učita dinamična stranica, aplikacija postavlja niz pitanja bazi: koji su najnoviji članci, koliko proizvoda ima u kategoriji, je li korisnik prijavljen, koje stavke su u korpi. Ako su ti upiti neefikasni, vrijeme se zbraja. Problem je što se to ne primijeti odmah. Stranica sa hiljadu redova u tabeli radi brzo i sa lošim upitom. Tek kada tabela naraste na stotine hiljada redova, isti taj upit postaje katastrofa.

Tipični simptomi koje vidimo u praksi su sljedeći:

  • Stranica se učitava sporije kako vremenom raste količina podataka.
  • Određene sekcije, poput pretrage ili filtriranja, primjetno zaostaju za ostatkom.
  • Opterećenje servera skoči u trenucima kada više korisnika istovremeno pristupa istom sadržaju.
  • Administrativni dio sajta postaje nepodnošljivo spor iako javni dio izgleda u redu.

U gotovo svim ovim slučajevima rješenje nije jači server, nego pametnija struktura podataka i bolji upiti.

Indeksi: najveći dobitak za najmanje truda

Ako postoji jedna stvar koja najčešće transformiše performanse, to su indeksi. Indeks u bazi funkcioniše slično kao indeks na kraju knjige. Umjesto da pretražuje svaku stranicu, baza odlazi direktno na lokaciju koja je joj potrebna. Bez indeksa, baza radi takozvani full table scan, odnosno čita svaki red da bi pronašla onaj koji odgovara uslovu.

Praktično pravilo glasi: kolone koje se koriste u WHERE, JOIN i ORDER BY klauzulama su prvi kandidati za indeksiranje. To uključuje strane ključeve, kolone za status, datume po kojima sortirate i polja po kojima korisnici pretražuju.

Kada indeks ne pomaže ili čak šteti

Indeksi nisu besplatni. Svaki indeks zauzima prostor i usporava operacije upisa, jer baza mora ažurirati i indeks pri svakom INSERT ili UPDATE. Zbog toga nema smisla indeksirati svaku kolonu. Tabela sa previše indeksa može biti sporija pri pisanju nego što dobija na čitanju. Cilj je ravnoteža: indeksirajte ono što se zaista često koristi za filtriranje i sortiranje, a ostalo ostavite.

Pronalaženje sporih upita

Ne možete popraviti ono što ne mjerite. Prije bilo kakve optimizacije treba znati koji upiti zaista oduzimaju vrijeme. Većina baza ima alate upravo za to.

  1. Slow query log. MySQL i MariaDB mogu bilježiti svaki upit koji traje duže od zadanog praga, recimo jedne sekunde. To je najbrži način da otkrijete najgore prijestupnike.
  2. EXPLAIN. Stavljanjem ključne riječi EXPLAIN ispred upita baza pokazuje svoj plan izvršavanja: koristi li indeks, koliko redova procjenjuje da će pročitati i radi li puni sken tabele.
  3. Profilisanje na nivou aplikacije. Alati za debagiranje pokazuju koliko upita generiše jedna stranica i koliko svaki traje. Često se otkrije da ista stranica izvršava isti upit desetine puta.

Upravo ta posljednja tačka vodi nas do jednog od najčešćih problema u modernim aplikacijama.

Problem N+1 upita

N+1 problem nastaje kada aplikacija izvrši jedan upit da dohvati listu stavki, a zatim za svaku stavku posebno izvrši dodatni upit. Ako prikazujete dvadeset proizvoda i za svaki posebno tražite ime kategorije, dobijate jedan upit za listu plus dvadeset dodatnih. To je dvadeset jedan upit umjesto dva.

Ovaj obrazac je podmukao jer se često krije iza ORM biblioteka koje učine pisanje koda lakim, ali sakriju koliko se upita zapravo izvršava u pozadini. Rješenje je takozvani eager loading, gdje unaprijed dohvatate povezane podatke jednim ili nekoliko upita umjesto desetinama malih. Razlika u brzini zna biti dramatična, naročito na stranicama sa listama.

Keširanje i denormalizacija

Najbrži upit je onaj koji uopšte ne morate izvršiti. Ako se određeni podatak rijetko mijenja, a često čita, nema razloga da ga baza ponovo izračunava pri svakom zahtjevu. Tu na scenu stupa keširanje.

  • Keširanje rezultata upita. Rezultat skupog upita čuva se u memoriji, na primjer u alatima poput Redisa, i poslužuje se direktno dok se podaci ne promijene.
  • Keširanje na nivou stranice. Cijeli generisani HTML čuva se i poslužuje bez ijednog poziva bazi za posjetioce koji vide isti sadržaj.
  • Denormalizacija. Ponekad ima smisla namjerno duplirati podatak, na primjer čuvati broj komentara direktno uz članak umjesto da ga brojite pri svakom prikazu. Plaćate malo dodatnim prostorom i složenošću upisa, a dobijate mnogo na brzini čitanja.

Ključno je razumjeti kada koja tehnika ima smisla. Keširanje koje se ne osvježava na vrijeme dovodi do prikaza zastarjelih podataka, pa strategija invalidacije keša mora biti dio plana od početka.

Praktične tehnike i njihov uticaj

Sljedeća tabela sažima najvažnije pristupe i kada ih primijeniti.

Tehnika Kada pomaže Na šta paziti
Dodavanje indeksa Spora filtriranja i sortiranja na velikim tabelama Usporava upis, troši prostor
Rješavanje N+1 Liste sa povezanim podacima Zahtijeva razumijevanje ORM-a
Keširanje upita Podaci koji se često čitaju, rijetko mijenjaju Invalidacija keša
Denormalizacija Skupi agregatni izračuni Dodatna složenost pri upisu
Dohvatanje samo potrebnih kolona Tabele sa mnogo kolona Izbjegavajte SELECT *

Posljednja stavka zaslužuje naglasak. Upit koji vraća sve kolone prenosi i podatke koji vam na toj stranici uopšte ne trebaju. Navođenjem tačno onih kolona koje koristite smanjujete količinu podataka koja putuje od baze do aplikacije, a to se osjeti na stranicama sa mnogo redova.

Optimizacija baze kao dio cjeline

Optimizacija baze nije jednokratan zahvat nego dio kontinuiranog rada na zdravlju sistema. Kako sadržaj raste i mijenja se način korištenja, ono što je nekad bilo dovoljno brzo postaje usko grlo. Zato je važno graditi aplikacije sa mjerljivom strukturom od početka i redovno pratiti kako se upiti ponašaju u stvarnim uslovima, a ne samo na praznoj bazi tokom razvoja.

U NEVIS-u ovaj pristup ugrađujemo u svaki projekat, od jednostavnih prezentacijskih sajtova do složenih sistema sa velikim katalozima. Ako razvijate web shop sa hiljadama proizvoda ili planirate web platformu koja mora podnijeti rast, dobro postavljena baza je temelj koji odlučuje hoće li stranica ostati brza i za godinu dana. Brzina nije luksuz nego dio osnovnog korisničkog iskustva i faktor koji pretraživači uzimaju u obzir pri rangiranju.

Ako vam stranica usporava kako raste, a niste sigurni gdje je problem, rado ćemo pogledati strukturu i upite te predložiti konkretne korake. Slobodno nas kontaktirajte i krenimo od mjerenja, a ne od nagađanja.

Prethodni Verzionisanje koda u web projektima: temelj svakog ozbiljnog rada
Sljedeći Web standardi i zašto su važni za svaki projekt