Ovladavanje performansama baze podataka: Iznad jednostavnog podešavanja, prema prediktivnoj arhitekturi
Većina usporavanja baza podataka ne počinje kao problem baze podataka. Počinju kao naizgled bezopasni izbori u aplikaciji: krajnja točka koja učitava jednu dodatnu relaciju po retku, upit nadzorne ploče koji raste sa svakim novim korisnikom, politika ponovnih pokušaja koja kratkotrajni prekid pretvara u oluju povezivanja. Dodavanje indeksa ili povećanje memorije može pomoći, ali samo podešavanje je reaktivna disciplina. Predvidljive performanse proizlaze iz arhitekture koja skup rad čini vidljivim, ograničenim i namjernim.
Ta je razlika važna zato što se baza podataka nalazi u središtu mnogih suprotstavljenih zahtjeva. API-ji trebaju nisku latenciju. Pozadinski poslovi trebaju propusnost. Izvještavanje treba opsežna čitanja. Upisima je potrebna ispravnost. Cilj nije baza podataka koja je apstraktno „brza”; cilj je sustav čije radno opterećenje ostaje razumljivo kako se mijenjaju promet, količina podataka i složenost proizvoda.
Počnite s oblikom radnog opterećenja
Prije izmjene upita opišite što sustav traži od baze podataka. Koji su zahtjevi osjetljivi na latenciju? Koje se operacije mogu izvršavati asinkrono? Jesu li čitanja uglavnom dohvaćanja pojedinačnih zapisa, filtrirani popisi, agregati ili pretraživanja cijelog teksta? Ima li jedan zakupac radikalno više podataka od drugoga?
To sprječava čestu pogrešku: optimiziranje upita bez razumijevanja nalazi li se na kritičnom putu. Izvještaj koji se pokreće jednom preko noći ima drukčije zahtjeve od zahtjeva za naplatu koji drži otvorenu transakciju. Oba zaslužuju ispravnost, ali ne bi trebala konkurirati za isti cilj odzivnosti.
Korisna pitanja uključuju:
- Koja je očekivana veličina rezultata za ovu krajnju točku?
- Kako se trošak upita mijenja kada tablica naraste deset puta?
- Filtrira li upit prije spajanja ili najprije stvara velik međurezultat?
- Može li se ovaj rad predmemorirati, unaprijed izračunati, paginirati ili premjestiti u radnik?
- Što se događa kada je ovisnost spora ili privremeno nedostupna?
Ta pitanja pretvaraju performanse iz zbirke trikova u naviku dizajnerskog pregleda.
Upotrebljavajte indekse za podršku obrascima pristupa
Indeks nije opći prekidač za brzinu. To je podatkovna struktura koja pomaže određenim predikatima, spajanjima i poredcima. Dodavanje indeksa bez ispitivanja stvarnih obrazaca upita može povećati trošak upisa, zauzeti prostor za pohranu i ostaviti spori put nepromijenjenim.
Razmotrite API koji prikazuje nedavne narudžbe kupca. Obrazac pristupa obično je „filtriraj po kupcu, sortiraj po vremenu stvaranja, vrati ograničenu stranicu”. Složeni indeks trebao bi odražavati taj obrazac, uzimajući u obzir optimizator sustava za upravljanje bazom podataka i točan oblik upita.
SELECT id, status, total, created_at
FROM orders
WHERE customer_id = :customer_id
ORDER BY created_at DESC
LIMIT :limit;
Važan dio nije pamćenje recepta za indeks. Važno je provjeriti plan izvršavanja i potvrditi da odabrani indeks smanjuje rad koji baza podataka obavlja. Planovi otkrivaju skenira li mehanizam daleko više redaka nego što se očekivalo, izvodi li skupo sortiranje ili bira redoslijed spajanja koji više ne odgovara distribuciji podataka.
Indeksi također zahtijevaju disciplinu održavanja. Kada krajnja točka promijeni filtre ili sortiranje, ponovno provjerite relevantne indekse. Kada se predlaže novi indeks, pitajte koji će upisi sada plaćati za njega. Zdrava shema usklađena je s trenutačnim radnim opterećenjem, a ne ukrašena svakim indeksom koji je ikada zvučao uvjerljivo.
Spriječite eksplozije upita na razini aplikacije
Mnoge PHP aplikacije susreću svoje prvo ozbiljno usko grlo baze podataka kroz obrazac upita N+1. Kod dohvaća stranicu nadređenih zapisa, a zatim izdaje jedan dodatni upit po zapisu radi učitavanja relacije. U ORM-u može izgledati čisto, no popis od 100 stavki može postati 101 povratno putovanje do baze podataka.
Rješenje je obično unaprijed učitavanje, pažljivo osmišljeno spajanje ili grupirano dohvaćanje. Pravi izbor ovisi o količini potrebnih povezanih podataka i o tome duplicira li rezultirajući upit retke. Načelo je jednostavno: broj operacija nad bazom podataka neka raste s zahtjevom, a ne sa svakom stavkom koju zahtjev vraća.
$orders = Order::query()
->where('customer_id', $customerId)
->with('lineItems')
->orderByDesc('created_at')
->limit(50)
->get();
Unaprijed učitavanje nije automatski besplatno. Učitavanje velike relacije za svaki redak može jednostavno zamijeniti mnogo malih upita jednim prevelikim rezultatom. Odaberite samo potrebne stupce, nametnite ograničenja na granici API-ja i izbjegavajte vraćanje neograničenih kolekcija. Performanse i održivost često se poboljšavaju zajedno kada krajnja točka ima precizan ugovor.
Oblikujte transakcije kao kratke, namjerne granice
Transakcije štite invarijante, ali dugotrajne transakcije mogu držati zaključavanja, zadržavati stare verzije redaka i blokirati koristan rad. Pouzdano je pravilo zadržati dio transakcije koji se odnosi na bazu podataka uskim. Gdje je moguće, provjerite ulaze prije ulaska u nju i ne pozivajte udaljene usluge dok je transakcija otvorena.
Na primjer, stvaranje narudžbe može zahtijevati rezerviranje zaliha i bilježenje namjere plaćanja. Transakcija baze podataka trebala bi zaštititi prijelaze lokalnog stanja. Komunikacija s vanjskim pružateljem plaćanja pripada tijeku rada osmišljenom za ponovne pokušaje i usklađivanje, a ne unutar transakcije koja čeka mrežu.
To prirodno vodi do idempotentnosti. API-ji i redovi poslova ponovno pokušavaju jer se kvarovi događaju nakon slanja zahtjeva, ali prije primitka odgovora. Operacijama upisa dodijelite ključ idempotentnosti ili drugi stabilan poslovni identifikator, provedite jedinstvenost gdje je primjereno i učinite ponovljenu isporuku sigurnom. Ponovni pokušaj ne bi smio stvoriti drugu narudžbu samo zato što je prvi odgovor izgubljen.
Odvojite operativna čitanja od analitičke potrebe
Transakcijske baze podataka izvrsne su za posluživanje neposrednog stanja proizvoda. Često su loše mjesto za pokretanje opsežne, česte analitike nad aktivnim tablicama. „Jednostavan” agregat može konkurirati prometu korisnika kada temeljni podaci postanu dovoljno veliki.
Počnite s umjerenim razdvajanjem: predmemorirajte skupe, ali stabilne sažetke, održavajte brojače ili modele čitanja kada zahtjevi dosljednosti to dopuštaju i rasporedite težak rad izvještavanja izvan kritičnih putanja zahtjeva. Kako potrebe rastu, može biti opravdan namjenski cjevovod za izvještavanje ili analitičko spremište. Odluka bi trebala slijediti dokaze o radnom opterećenju, a ne modu.
Isto razmišljanje vrijedi za predmemoriranje. Predmemorirajte vrijednosti s jasnom strategijom poništavanja i definiranim vlasnikom. Nemojte koristiti predmemoriju da sakrijete neograničen upit ili nejasan model podataka. Predmemorija bi trebala smanjiti poznat, ponovljiv rad; ne bi trebala postati druga baza podataka čiju ispravnost nitko ne može objasniti.
Učinite ponašanje u produkciji vidljivim
Prediktivna arhitektura ovisi o povratnim informacijama. Pratite latenciju zahtjeva, korištenje veza s bazom podataka, spore upite, stope pogrešaka, dubinu reda i vrijeme koje poslovi provode čekajući u odnosu na izvršavanje. Povežite aktivnost baze podataka s nazivima krajnjih točaka, vrstama poslova i verzijama implementacije gdje vam alati to dopuštaju.
Upravljanje vezama zaslužuje posebnu pozornost u implementacijama u kontejnerima. Svaki PHP radnik ili dugotrajni proces može držati veze, a skaliranje aplikacijskih kontejnera bez razmatranja ograničenja veza baze podataka može ga brzo iscrpiti. Veličinu skupova odredite konzervativno, veze pravilno zatvarajte ili vraćajte i osigurajte da se baza podataka tretira kao ograničen zajednički resurs, a ne kao elastična lokalna usluga.
U okruženjima temeljenima na Dockeru, pokretanje aplikacije i spremnost baze podataka također su različiti događaji. Proces kontejnera može se pokrenuti prije nego što baza podataka prihvati veze. Aplikacije bi trebale rukovati prolaznim neuspjesima povezivanja ograničenim ponovnim pokušajima i postupnim povećanjem čekanja, dok bi implementacije trebale provjeravati spremnost odgovarajućim zdravstvenim provjerama. Beskonačni ponovni pokušaji nisu otpornost; mogu odgoditi oporavak i pojačati opterećenje tijekom prekida.
Performanse su proizvod granica
Najtrajnija poboljšanja baze podataka rijetko dolaze od jedne dramatične promjene konfiguracije. Dolaze od granica: ograničenih skupova rezultata, kratkih transakcija, eksplicitnog ponašanja pri ponovnim pokušajima, svrhovitih indeksa, odvojenih radnih opterećenja i vidljivih uskih grla.
To je prijelaz s podešavanja na arhitekturu. Podešavanje pita: „Kako ovaj upit danas može raditi brže?” Prediktivna arhitektura pita: „Što sprječava da ovo radno opterećenje sutra postane iznenađujuće?” Drugo pitanje stvara sustave koji nisu samo brži pod pritiskom, već su i sljedećem razvojnom programeru lakši za razumijevanje, upravljanje i pouzdano mijenjanje.