Подесување на перформансите на PHP: Одете подалеку од профилирањето, совладајте ги вашите тесни грла
Повеќето проблеми со перформансите на PHP не започнуваат со бавен PHP. Тие започнуваат со нецелосна слика за тоа каде се троши времето.
Лесно е да отворите профајлер, да пронајдете скапа функција и да ја оптимизирате. Понекогаш тоа функционира. Почесто, видливата функција е само местото каде што вашата апликација чека: на барање до база на податоци, мрежен повик, натпреварување за пристап до датотечниот систем, заклучување на сесијата, заситен PHP-FPM pool или неефикасна конфигурација за распоредување.
Профилирањето останува суштинско, но е само почеток. Добрата работа на перформансите значи да го следите барањето низ целиот систем, да го идентификувате ограничувачкиот ресурс и да направите промена што го подобрува исходот за корисникот без да ја направи кодната база кревка.
Започнете со мерлив симптом што го забележува корисникот
„Апликацијата делува бавно“ не е дијагноза. Дефинирајте го релевантното барање, неговото очекувано оптоварување и метриката што не ги исполнува очекувањата. За API, тоа може да бидат латентноста на опашката и стапката на грешки. За batch job, тоа може да бидат времето на завршување и користењето меморија. За процес на наплата, тоа може да биде времето од пристигнувањето на барањето до употреблив одговор.
Мерете повеќе од просекот. Просеците можат да ги сокријат болните барања што корисниците ги паметат. Одделете ги брзите и бавните патеки, а потоа споредете ги нивните траги, бројот на барања, големините на payload-ите и надворешните зависности. Ако една крајна точка е бавна само при конкурентност, локалниот профил на едно барање може да биде целосно здрав, додека продукцискиот систем не е.
Класифицирајте го тесното грло пред да оптимизирате
Секое барање троши некоја комбинација од CPU, меморија, капацитет на базата на податоци, мрежно време и капацитет на споделени worker-и. Корисното прашање не е „која PHP линија е бавна?“, туку „кој ресурс го ограничува ова барање токму сега?“
- Работата ограничена од CPU вклучува скапи трансформации, компресија, криптографија, обработка на слики и неефикасни циклуси низ големи колекции.
- Работата ограничена од базата на податоци вклучува недостасувачки индекси, прекумерен број повратни патувања, натпреварување за заклучувања, лоши облици на барања и преземање многу повеќе податоци отколку што е потребно.
- Работата ограничена од I/O вклучува HTTP повици, редици, object storage, DNS резолуција, бавни дискови и оддалечени услуги.
- Работата ограничена од капацитет се појавува кога PHP-FPM worker-и, конекции со базата на податоци, CPU јадра или меморија се исцрпени под оптоварување.
Оваа разлика ја менува поправката. Кеширањето на пресметка што троши многу CPU може да помогне. Кеширањето околу заклучување во база на податоци може да прикрие проблем со исправноста. Зголемувањето на бројот на PHP worker-и може да го подобри пропусниот опсег сè додека притисокот врз меморијата или натпреварувањето во базата на податоци не го претворат подобрувањето во каскада на неуспеси.
Направете ја работата со базата на податоци видлива
Во типични PHP апликации со рендерирање на серверот и интензивна употреба на API, базата на податоци заслужува рано внимание. Бројте ги барањата по барање, бележете ги бавните барања и проверувајте ги плановите за извршување за барањата што доминираат во времето или оптоварувањето. Барање што е прифатливо еднаш може да стане сериозен трошок кога се извршува стотици пати во крајна точка за листа.
Елиминирајте го случајното умножување на барања
Класичниот пример е вчитување поврзани податоци во циклус. Кодот може да изгледа уредно, но претвора едно барање во едно барање за родителските записи плус едно барање по родител.
$orders = $repository->findRecentOrders();
foreach ($orders as $order) {
$customer = $repository->findCustomer($order->customerId());
// render customer data
}
Претпочитајте стратегија за барање или вчитување податоци што ја презема потребната релација во ограничен број операции. Потоа проверете дали резултатот не е претерано широк. Преземањето на секоја колона, секоја релација и секој историски запис може да замени N+1 проблем со мемориски проблем.
Индексите треба да поддржуваат реални обрасци на пристап: филтрирање, спојување, сортирање и пагинација. Не ги додавајте наслепо. Секој индекс има трошоци за запишување и складирање, а индекс што не одговара на предикатот и редоследот на барањето можеби нема да го реши проблемот што сте го измериле.
Намалете го чекањето, не само пресметувањето
PHP worker што чека оддалечена услуга сè уште е зафатен. При доволно конкурентен сообраќај, тоа чекање го троши worker pool-от и претвора мало забавување на зависност во широка латентност на апликацијата.
Поставете експлицитни временски ограничувања за појдовните повици. Временското ограничување не е непријатност; тоа е граница што го заштитува остатокот од апликацијата. Намерно изберете временски ограничувања за поврзување и вкупно време, предвидливо справувајте се со неуспесите и избегнувајте веднаш да го повторувате секој неуспех. Повторните обиди можат да го засилат оптоварувањето на веќе нездрава зависност.
Кога на барањето не му е потребен надворешен резултат синхроно, преместете ја работата во редица и вратете одговор што точно ја одразува новата состојба. Ова не е универзален одговор: на корисниците сè уште им се потребни сигурен статус, идемпотентни задачи и справување со неуспеси. Но често е подобра архитектура отколку да се принудува веб-барање да координира долг синџир од оддалечени операции.
Користете го кеширањето како договор
Кеширајте го резултатот од скапа, повторлива работа кога можете да објасните три работи: што се кешира, кога застарува и како се инвалидира или безбедно истекува. „Додадете Redis“ не е стратегија за кеширање.
Добри кандидати вклучуваат стабилна конфигурација, рендерирани фрагменти, референтни податоци и одговори со многу читања и јасен прозорец на свежина. Бидете внимателни со податоци специфични за корисникот, одговори чувствителни на авторизација и променлив инвентар или цени. Брз неточен одговор сè уште е неточен.
Исто така, заштитете се од cache stampede. Ако популарен клуч истече и многу барања го регенерираат истовремено, кешот може директно да го префрли оптоварувањето на базата на податоци или upstream услугата. Во зависност од системот, може да бидат соодветни кратко рандомизирано истекување, коалесцирање на барања или контролиран пристап stale-while-revalidate.
Разберете го капацитетот на PHP runtime-от
Поставките на PHP-FPM се продукциска архитектура, а не шаблонски код. Бројот на worker-и мора да одговара на меморијата достапна за контејнерот или хостот, оставајќи резерва за оперативниот систем, веб-серверот, buffer-ите на клиентот за базата на податоци и други процеси. Поставувањето многу високо ограничување на worker-и може да создаде повеќе конкурентност отколку што можат да поднесат downstream системите.
Мерете ја заситеноста на worker-ите, чекањето во редица, растот на меморијата и траењето на барањата при реалистично оптоварување. Ако worker-ите се зафатени поради бавни барања до базата на податоци, додавањето worker-и ретко ја поправа основната причина. Ако се неактивни, но барањата чекаат во редица кај proxy, проверете ги ограничувањата на конекциите и конфигурацијата за распоредување. Ако меморијата расте низ барањата, истражете го задржувањето на ниво на апликација, создавањето големи одговори и однесувањето на екстензиите, наместо да претпоставите дека garbage collection ќе го реши проблемот.
Во контејнери, направете ги ограничувањата на ресурсите експлицитни и тестирајте со ограничувања слични на продукциските. Процес што работи на лаптоп на развивач може нагло да откаже кога контејнерот ќе го достигне меморискиот плафон.
Одржувајте ја оптимизираната патека
Промените за перформанси често ја зголемуваат сложеноста: групно вчитување, кешови, асинхрони работни текови и пристап до податоци на пониско ниво воведуваат нови начини на откажување. Направете ги тие компромиси видливи во кодот и операциите. Именувајте ги кеш-клучевите доследно, централизирајте ги политиките за временски ограничувања, евидентирајте ги неуспесите на зависностите со корисен контекст и додајте тестови за однесувањето при инвалидирање и повторни обиди.
Мерете ги промените во однос на репрезентативно оптоварување, а не на едно среќно барање. Потврдете го подобрувањето во метриката што ја мотивираше работата, а потоа следете за регресии во меморијата, оптоварувањето на базата на податоци и справувањето со грешки. Помало време на одговор што ги удвојува запишувањата во базата на податоци не е автоматски победа.
Перформансите се својство на системот
Најсилните PHP апликации не се оние со најумните микрооптимизации. Тие се оние што ја прават работата видлива, избегнуваат непотребна работа, го ограничуваат чекањето што не можат да го избегнат и откажуваат достоинствено кога зависностите имаат проблеми.
Користете профилирање за да најдете докази, но продолжете да ги следите тие докази надвор од PHP call stack-от. Највредното тесно грло ретко е она што носи најдраматична промена во кодот. Тоа е она што ја подобрува целата патека од пристигнато барање до сигурен одговор.