Preispitajte arhitekturu svoje baze podataka: neopjevani junak pozadinskih performansi
Mnogi problemi s performansama pozadinskog sustava dolaze prerušeni u probleme aplikacije. Sporoj krajnjoj točki doda se još jedan sloj predmemorije. Upit nadzorne ploče dobije veće vremensko ograničenje. Skup PHP radnika podešava se dok ne postane skup i krhak. Ponekad te promjene pomažu. Često samo skrivaju stvarno ograničenje: arhitektura baze podataka više ne odgovara načinu na koji se sustav koristi.
Dizajn baze podataka rijetko je glamurozan. Ne stvara dramatičan demo i možda se ne pojavljuje na planu razvoja proizvoda. Ipak, određuje koliko pouzdano API odgovara, koliko sigurno tim može isporučivati promjene i ostaje li aplikacija razumljiva kako podaci i promet rastu. Tretiranje toga kao detalja implementacije jedan je od najpouzdanijih načina za stvaranje pozadinskog sustava koji djeluje brzo tijekom razvoja, a nepredvidivo u produkciji.
Performanse počinju oblikom podataka
Prije optimizacije koda postavite jednostavnije pitanje: koji su podaci ovom zahtjevu stvarno potrebni i kako su ti podaci pohranjeni? Odgovor utječe na svaki sloj iznad njega.
Razmotrite API krajnju točku koja vraća narudžbu zajedno s kupcem, stavkama, plaćanjima i statusom isporuke. Naivna ORM implementacija može taj naizgled jednostavan odgovor pretvoriti u niz upita: jedan za narudžbu, jedan za kupca, a zatim jedan upit po povezanoj kolekciji ili zapisu. To je poznati problem N+1 upita, no šira je pouka važnija: pogodnost na razini aplikacije može prikriti rad na razini baze podataka.
Dobra arhitektura čini uobičajene obrasce pristupa jeftinima, a neuobičajene obrasce pristupa eksplicitnima. To ne znači duplicirati podatke posvuda ili prerano dijeliti baze podataka. Znači dizajnirati tablice, odnose i granice upita oko operacija koje aplikacija najčešće izvodi.
- Pohranjujte transakcijske činjenice u normaliziranom obliku kada su važni ispravnost i ažuriranja.
- Dodajte indekse koji podržavaju stvarne obrasce filtriranja, spajanja i sortiranja.
- Stvorite namjenske modele za čitanje kada radno opterećenje izvještavanja ili pretraživanja ne bi trebalo konkurirati transakcijskom prometu.
- Mjerite broj upita, vrijeme upita, pregledane retke i ponašanje zaključavanja umjesto da se oslanjate na intuiciju.
Indeksi su odluke o proizvodu, a ne samo postavke baze podataka
Indeks je obećanje o tome kako će se podaci pronaći. Može selektivno pretraživanje pretvoriti u brzu operaciju, ali također povećava troškove pohrane i pisanja. Svako umetanje, ažuriranje ili brisanje možda će ga trebati održavati. Pravo pitanje nije „treba li ovaj stupac indeksirati?”, nego „koji upiti moraju ostati brzi kako tablica raste?”
Pretpostavimo da API često dohvaća nedavno plaćene račune za jedan račun:
SELECT id, total, issued_at
FROM invoices
WHERE account_id = ?
AND status = 'paid'
ORDER BY issued_at DESC
LIMIT 50;
Složeni indeks koji počinje s account_id i status, nakon kojih slijedi issued_at, može se dobro uskladiti s tim obrascem pristupa. No indeks s istim stupcima drukčijim redoslijedom može biti manje koristan. Baza podataka mora učinkovito suziti skup kandidata prije nego što može proizvesti traženi redoslijed.
Nemojte dodavati indekse samo zato što se stupac pojavljuje u upitu. Pregledajte plan upita, testirajte s realističnim količinama podataka i uzmite u obzir pisanja. Tablicu s desetak spekulativnih indeksa može biti sporije održavati i teže razumjeti nego tablicu s malim skupom namjerno odabranih indeksa.
Držite transakcije kratkima, a granice jasnima
Transakcije štite dosljednost, ali nisu besplatne. Dugotrajne transakcije mogu držati zaključavanja, zadržavati stare verzije redaka, povećavati sukobljavanje i otežavati oporavak od kvarova. To je posebno lako previdjeti u PHP aplikacijama, gdje zahtjev može obavljati rad s bazom podataka, pozvati vanjski API, prikazati odgovor i poslati obavijest u jednom linearnom toku.
Transakcija baze podataka obično bi trebala sadržavati samo promjene koje moraju uspjeti ili ne uspjeti zajedno. Nemojte je držati otvorenom tijekom mrežnog zahtjeva ili čekanja pružatelja e-pošte. Najprije postojano spremite nužno stanje, potvrdite ga, a zatim pokrenite vanjski rad putem trajnog mehanizma kao što je outbox zapis ili posao u redu čekanja.
$pdo->beginTransaction();
try {
$orderId = createOrder($pdo, $payload);
addOutboxEvent($pdo, 'order.created', ['order_id' => $orderId]);
$pdo->commit();
} catch (Throwable $e) {
$pdo->rollBack();
throw $e;
}
Ovaj obrazac ne uklanja složenost; smješta je tamo gdje se njome može promišljeno upravljati. Radnik može ponovno pokušati isporučiti outbox događaj. Zahtjev može vratiti odgovor nakon što se trajno stanje potvrdi. Ako dođe do ponovnog pokušaja, potrošači bi trebali biti idempotentni, što znači da se isti događaj može obraditi više puta bez stvaranja dvostrukih poslovnih učinaka.
Odvojite operativne upite od analitičkih potreba
Od iste se baze podataka često traži da služi dvjema suprotstavljenim svrhama: dovršiti radnju korisnika u milisekundama i odgovoriti na otvorena pitanja o mjesecima ili godinama poslovne povijesti. Prvo radno opterećenje cijeni predvidljivu latenciju i male, selektivne upite. Drugo može pregledavati velike raspone, intenzivno agregirati i tolerirati sporije odgovore.
Njihovo kombiniranje bez zaštitnih mjera poziva probleme. Složeni upit za izvještavanje može potrošiti memoriju, I/O i veze potrebne za naplatu, autentifikaciju ili drugi rad usmjeren na korisnike. Neposredni instinkt može biti dodati više kapaciteta baze podataka, no arhitektonsko razdvajanje može biti dugotrajnije rješenje.
Ovisno o sustavu, to može značiti repliku za čitanje, bazu podataka za izvještavanje osvježavanu kontroliranim prijenosom podataka, unaprijed izračunate agregate ili asinkroni izvozni cjevovod. Odgovarajući izbor ovisi o zahtjevima dosljednosti. Financijski zaslon može trebati točnu transakcijsku istinu; upravljačka nadzorna ploča može prihvatiti kratko kašnjenje u zamjenu za stabilne performanse u produkciji.
Učinite dosljednost svjesnim ugovorom
Timovi često kažu da trebaju podatke „u stvarnom vremenu” kada zapravo misle „dovoljno nedavne za ovu odluku”. To su različiti zahtjevi. Definirajte koja čitanja moraju odmah vidjeti potvrđeno pisanje, a koja mogu biti eventualno dosljedna. Kada je taj ugovor eksplicitan, predmemoriranje, replike, redovi čekanja i denormalizirane projekcije postaju inženjerski alati umjesto izvora slučajnih grešaka.
Dizajnirajte API-je tako da izbjegnete slučajan rad baze podataka
Arhitektura baze podataka i dizajn API-ja nerazdvojivi su. Krajnja točka koja klijentima dopušta zahtijevati proizvoljne ugniježđene odnose, neograničene veličine stranica ili neograničene datumske raspone predaje planiranje upita svakom korisniku API-ja. To stvara neizvjesnost u pogledu performansi i znatno otežava planiranje kapaciteta.
Koristite paginaciju sa stabilnim redoslijedom, provjeravajte kombinacije filtara, ograničite skupe raspone i namjerno izložite oblike odgovora. Za krajnje točke s poznatim potrebama klijenata, ciljani je odgovor obično bolji od generičkog grafa podataka koji zahtijeva da poslužitelj razriješi svaki mogući odnos.
Predmemoriranje može pomoći, osobito za ponovljena čitanja podataka koji se rijetko mijenjaju. No predmemoriranje nije dopuštenje za ignoriranje neučinkovitih upita. Hladna predmemorija, događaj poništavanja ili novi obrazac pristupa na kraju će otkriti temeljni dizajn. Najprije izgradite razuman upit; zatim koristite predmemoriranje ondje gdje to ugovor o svježini dopušta.
Docker bi trebao olakšati reprodukciju ponašanja baze podataka
Spremnici su vrijedni kada smanjuju razlike tipa „radi na mom računalu”, a ne kada lokalni razvoj pretvaraju u minijaturni produkcijski klaster. Postavljanje Docker Composea može timu pružiti dosljednu verziju baze podataka, konfiguraciju i put inicijalizacije. Također bi migracije trebalo učiniti uobičajenim dijelom radnog tijeka.
Držite promjene sheme verzioniranima i pažljivo ih implementirajte. Aditivne promjene općenito su sigurnije: stvorite stupac koji dopušta NULL, prema potrebi popunite podatke u kontroliranim skupinama, ažurirajte kod aplikacije, a zatim pooštrite ograničenja nakon što stari kod više ne koristi prethodni oblik. Destruktivne promjene zahtijevaju dodatnu pažnju jer postupna implementacija može privremeno pokretati više verzija aplikacije nad istom shemom.
Sigurnosne kopije i testovi vraćanja također pripadaju ovom razgovoru. Sigurnosna kopija koja nikada nije vraćena pretpostavka je, a ne plan oporavka. Arhitektura uključuje operativni put za rješavanje pogrešaka, oštećenih podataka i neuspjelih implementacija.
Trajna prednost je jasnoća
Najbolja arhitektura baze podataka nije ona uz koju je vezano najviše tehnologija. To je ona čije su vlasništvo nad podacima, obrasci upita, pravila dosljednosti i ponašanje pri kvarovima dovoljno jasni da ih tim može sigurno mijenjati.
Kada se krajnja točka uspori, pogledajte ispod PHP koda prije dodavanja još jednog sloja iznad njega. Ispitajte upit, indeks, transakciju, količinu podataka i konkurentsko radno opterećenje. Ta navika pretvara rad na performansama iz hitnog podešavanja u promišljen dizajn sustava. Baze podataka možda su neopjevani junak performansi pozadinskog sustava, ali su i mjesto gdje pragmatično inženjerstvo postaje vidljivo: u softveru koji ostaje responzivan, ispravan i održiv dugo nakon isporuke prve verzije.