Performanse PHP-a: Izvan mjerila, prema zdravlju sustava u stvarnom svijetu
Performanse PHP-a često se svodi na utrku: koji okvir obrađuje više zahtjeva u sekundi, koje izvođenje pobjeđuje u sintetičkom benchmarku, koja mala izmjena koda skida nekoliko milisekundi. Te usporedbe mogu biti korisne, ali rijetko su mjesto na kojem produkcijski sustavi uspijevaju ili padaju.
Spora aplikacija obično nije „spor PHP”. To je sustav koji troši vrijeme negdje: čeka zaključavanje baze podataka, opetovano poziva drugu uslugu, ponovno izgrađuje podatke koji bi se mogli ponovno upotrijebiti, iscrpljuje kapacitet radnika ili obavlja daleko više posla nego što zahtjev zahtijeva. Najvrjedniji rad na performansama počinje pronalaženjem tog čekanja ili rasipanja, a zatim poboljšavanjem cijelog puta umjesto dotjerivanja izolirane linije koda.
Mjerite zahtjev, a ne reputaciju
Prije izmjene koda definirajte problem vidljiv korisniku. Je li stranica spora na 95. percentilu? Istječu li vremenska ograničenja API zahtjeva tijekom skokova prometa? Raste li zaostatak u redu brže nego što ga radnici mogu obraditi? Uzrokuje li pritisak na memoriju ponovno pokretanje procesa?
Samo propusnost ne odgovara na ova pitanja. Usluga može obraditi mnogo brzih zahtjeva dok manja skupina skupih zahtjeva stvara neprihvatljivu latenciju. Može i izgledati zdravo sve dok se ovisnost ne uspori i svaki PHP radnik ne postane zauzet čekanjem na I/O.
Korisna mjerenja povezuju ponašanje aplikacije sa zdravljem sustava:
- Raspodjele vremena odgovora, posebno sporiji percentili umjesto samo prosjeka.
- Stope pogrešaka i isteka vremena, odvojene po krajnjoj točki ili vrsti posla.
- Trajanje upita baze podataka, broj, čekanja na zaključavanje i pritisak na skup veza.
- Latencija i stope neuspjeha vanjskih usluga.
- Dubina reda, starost posla, volumen ponovnih pokušaja i iskorištenost radnika.
- CPU, memorija, diskovni I/O i kapacitet procesa pri realističnom opterećenju.
Praćenje je osobito korisno jer nejasnu pritužbu pretvara u vremensku crtu. Zahtjev koji traje 900 milisekundi može sadržavati samo 40 milisekundi izvršavanja PHP-a. Preostalo vrijeme može biti niz upita baze podataka, mrežnih poziva i rada na serijalizaciji. Prvo optimiziranje PHP dijela donijelo bi tehnički ispravno, ali komercijalno nevažno poboljšanje.
Rad s bazom podataka obično je prvi ozbiljan sumnjivac
PHP olakšava izražavanje pristupa podacima, a ta praktičnost može sakriti skupo ponašanje. Klasičan je primjer obrazac N+1 upita: učitajte popis zapisa, zatim jednom upitajte povezane podatke za svaki zapis. Može biti nevidljiv s deset redaka, a bolan s nekoliko stotina.
Rješenje nije uvijek „spojiti sve”. Široko spajanje može umnožiti retke, prenijeti nepotrebne stupce i otežati razumijevanje logike aplikacije. Cilj je odabrati promišljenu strategiju učitavanja: dohvatiti povezane podatke u skupinama, odabrati samo potrebna polja i indeksirati predikate koje stvarni upiti koriste.
Razmotrite API krajnju točku koja vraća narudžbe i njihove kupce. Umjesto učitavanja kupca unutar petlje, prikupite identifikatore kupaca i dohvatite ih jednim upitom. Zatim mapirajte rezultat u memoriji.
$customerIds = array_values(array_unique(array_column($orders, 'customer_id')));
$customers = $customerRepository->findByIds($customerIds);
$customersById = [];
foreach ($customers as $customer) {
$customersById[$customer->id] = $customer;
}
Ovaj obrazac smanjuje broj povratnih putovanja uz očuvanje čitljive granice između narudžbi i kupaca. I dalje ga treba provjeriti zapisnicima upita i podacima sličnima produkcijskima. Indeks koji pomaže razvojnoj bazi s nekoliko tisuća redaka možda neće odgovarati obrascu pristupa većoj tablici, a naizgled jednostavan filtar može postati skup kada sprječava korištenje indeksa.
Također tretirajte transakcije kao pitanje performansi. Neka budu uske, izbjegavajte mrežne pozive dok držite zaključavanja i jasno definirajte ponašanje ponovnih pokušaja tamo gdje se očekuju prolazni sukobi. Ponovni pokušaj bez idempotentne operacije može privremeni problem s bazom podataka pretvoriti u duplicirani rad ili duplicirane zapise.
Učinite udaljene pozive vidljivima i ograničenima
Svaki HTTP poziv, dohvat iz predmemorije, objava poruke i operacija nad datotekom uvodi ovisnost o nečemu izvan trenutačnog PHP procesa. Ti pozivi trebaju vremenska ograničenja, obradu neuspjeha i odluku o vlasništvu: treba li ovaj zahtjev čekati, vratiti djelomične informacije, koristiti predmemorirane podatke ili predati posao u red?
Nedostajuće vremensko ograničenje nije strpljenje; to je neograničeno zauzimanje kapaciteta radnika. Ako dovoljno zahtjeva čeka degradiranu uzvodnu uslugu, PHP-FPM radnici ili radnici aplikacije mogu se popuniti. Zdravi zahtjevi tada čekaju iza nezdravih, a lokalizirani problem ovisnosti postaje široki prekid rada.
Namjerno postavite vremenska ograničenja za povezivanje i cijeli zahtjev, razlikujte neuspjehe koji se mogu ponoviti od trajnih i ograničite ponovne pokušaje. Ponovni pokušaji trebaju koristiti odgodu i primjenjivati se samo kada je operaciju sigurno ponoviti. Na primjer, ponavljanje čitanja može biti razumno; ponavljanje stvaranja plaćanja zahtijeva mehanizam idempotentnosti koji primajući sustav poštuje.
Premjestite nehitni rad izvan kritičnog puta
Mnogi zahtjevi ne moraju izvršiti svaki popratni učinak prije odgovora. Slanje obavijesti, generiranje izvješća, sinkronizacija indeksa pretraživanja i obrada prijenosa često se bolje rješavaju asinkrono. To poboljšava percipiranu latenciju, ali mijenja dizajn: poslovi trebaju trajnu pohranu, vidljivost, pravila ponovnih pokušaja i jasno postupanje s radom koji opetovano ne uspijeva.
Red nije čarolija za performanse. Mijenja trenutačno vrijeme odgovora za eventualni dovršetak i operativnu odgovornost. Koristite ga ondje gdje je taj kompromis prihvatljiv i izložite dovoljno statusa da korisnici i timovi za podršku mogu razumjeti odgođeni rad.
Predmemorirajte rezultat skupih odluka
Predmemoriranje najbolje funkcionira kada je vezano uz jasan trošak i jasnu strategiju invalidacije. Predmemorirajte skup agregat, često traženi referentni skup podataka ili prikazani fragment koji se rijetko mijenja. Nemojte dodavati predmemoriju samo zato što se krajnja točka čini sporom; najprije utvrdite je li skupi rad dovoljno stabilan za ponovnu upotrebu.
Ključevi predmemorije trebaju uključivati svaki ulaz koji bitno mijenja rezultat, kao što su lokalizacija, opseg autorizacije, filtri ili verzija. Preširok ključ uzrokuje promašaje. Preuzak ključ može izložiti podatke jednog korisnika drugome ili vratiti netočan sadržaj.
Invalidacija je test dizajna. Ako se podaci promijene, odlučite hoćete li odmah invalidirati, ažurirati predmemoriranu vrijednost, koristiti kratko trajanje ili prihvatiti ograničenu zastarjelost. Ne postoji univerzalan odgovor, ali trebao bi postojati izričit. Za kritičnu ispravnost prednost dajte jednostavnijem putu, čak i ako je manje agresivan.
Kapacitet je dio dizajna aplikacije
PHP aplikacije često rade iza upravitelja procesa, platformske infrastrukture za kontejnere ili oboje. Broj radnika, ograničenja memorije, veze s bazom podataka i ograničenja resursa kontejnera čine jedan sustav. Povećanje broja PHP radnika može poboljšati konkurentnost samo ako nizvodne ovisnosti također imaju kapacitet. U suprotnom može stvoriti više sukoba u bazi podataka, više istodobnih udaljenih poziva i veću potrošnju memorije.
U implementacijama temeljenima na Dockeru zadržite konfiguraciju izvan aplikacije i osigurajte da spremnost ima smisla. Pokretanje kontejnera ne jamči da njegova aplikacija može sigurno posluživati promet. Provjere implementacije trebaju odražavati ono što usluzi stvarno treba, uz izbjegavanje krhkih provjera zbog kojih svaki zastoj ovisnosti ponovno pokreće inače zdrave procese.
Testiranje opterećenja najvrjednije je kada nalikuje stvarnosti: reprezentativne veličine korisnih podataka, autentificirani putovi, zagrijanost predmemorije, istodobni korisnici i sporije ovisnosti. Testirajte putanje neuspjeha kao i putanje uspjeha. Sustav koji dobro radi samo kada svaka usluga odgovara trenutačno nije testiran u svojim najvažnijim uvjetima.
Performanse su disciplina održavanja
Trajna korist nije jednokratna optimizacija. To je olakšavanje otkrivanja regresija performansi i otežavanje uvođenja skupog ponašanja. Neka broj upita bude vidljiv u razvoju i testovima gdje je praktično. Pregledavajte petlje koje izvode I/O. Dodajte nadzorne ploče za važne krajnje točke i redove. Postavite upozorenja na simptome koje korisnici osjećaju, a ne samo na aktivnost infrastrukture.
Dobre performanse PHP-a su smiren dizajn sustava: manje nepotrebnog rada, manje nekontroliranih čekanja, predvidivo ponašanje pri neuspjehu i odluke o kapacitetu utemeljene na dokazima. Benchmarkovi mogu ukazati na mogućnosti. Zdravlje sustava u stvarnom svijetu dokazuje da aplikacija ostaje brza kada njezini podaci rastu, ovisnosti se loše ponašaju i korisnici dolaze u isto vrijeme.