ИТ развој

PHP Performance: Beyond Benchmarks, Into Real-World System Health

PHP перформанси: надвор од бенчмарците, во здравјето на системите во реалниот свет

Перформансите на PHP често се сведуваат на трка: која рамка обработува повеќе барања во секунда, која извршна околина победува на синтетички бенчмарк, која мала промена во кодот скратува неколку милисекунди. Овие споредби можат да бидат корисни, но ретко токму таму продукциските системи успеваат или не успеваат.

Бавната апликација обично не е „бавен PHP“. Таа е систем што троши време некаде: чека заклучување во базата на податоци, постојано повикува друга услуга, повторно гради податоци што би можеле да се искористат повторно, го исцрпува капацитетот на работниците или извршува многу повеќе работа отколку што барањето бара. Највредната работа за перформанси започнува со пронаоѓање на тоа чекање или трошење, а потоа со подобрување на целата патека, наместо со дотерување на изолирана линија код.

Мерете го барањето, а не репутацијата

Пред да го менувате кодот, дефинирајте го проблемот видлив за корисникот. Дали страницата е бавна на 95-тиот перцентил? Дали API-барањата истекуваат за време на скокови во сообраќајот? Дали заостанувањето во редицата расте побрзо отколку што работниците можат да го обработат? Дали притисокот врз меморијата предизвикува процесите да се рестартираат?

Само пропусноста не одговара на овие прашања. Услугата може да обработува многу брзи барања, додека помала група скапи барања создава неприфатлива латентност. Исто така, може да изгледа здраво сè додека некоја зависност не се забави и секој PHP работник не стане зафатен со чекање на I/O.

Корисните мерења го поврзуваат однесувањето на апликацијата со здравјето на системот:

  • Распределби на времето на одговор, особено побавните перцентили наместо само просеците.
  • Стапки на грешки и истекување, раздвоени по крајна точка или тип на задача.
  • Траење, број, чекања за заклучување и притисок врз пулот на конекции на барањата кон базата на податоци.
  • Латентност и стапки на неуспех на надворешни услуги.
  • Длабочина на редицата, старост на задачите, обем на повторни обиди и искористеност на работниците.
  • CPU, меморија, дисков I/O и капацитет на процесите при реалистично оптоварување.

Следењето е особено корисно бидејќи нејасна жалба претвора во временска линија. Барање што трае 900 милисекунди може да содржи само 40 милисекунди PHP-извршување. Преостанатото време може да биде низа од барања кон базата на податоци, мрежни повици и работа со серијализација. Прво оптимизирање на PHP-делот би донело технички точен, но комерцијално незначаен напредок.

Работата со базата на податоци обично е првиот сериозен осомничен

PHP го олеснува изразувањето пристап до податоци, а таа погодност може да сокрие скапо однесување. Класичен пример е шемата на барања N+1: се вчитува листа записи, а потоа се бараат поврзани податоци еднаш за секој запис. Може да биде незабележливо со десет редови, а болно со неколку стотици.

Решението не е секогаш „спој сè“. Широк спој може да ги умножи редовите, да пренесе непотребни колони и да ја направи логиката на апликацијата потешка за разбирање. Целта е да се избере намерна стратегија за вчитување: преземајте поврзани податоци во серии, избирајте само неопходни полиња и индексирајте ги предикатите што ги користат вистинските барања.

Разгледајте API-крајна точка што враќа нарачки и нивните клиенти. Наместо да вчитувате клиент во јамка, соберете ги идентификаторите на клиентите и преземете ги со едно барање. Потоа мапирајте го резултатот во меморија.

$customerIds = array_values(array_unique(array_column($orders, 'customer_id')));

$customers = $customerRepository->findByIds($customerIds);

$customersById = [];
foreach ($customers as $customer) {
    $customersById[$customer->id] = $customer;
}

Оваа шема ги намалува повратните патувања, притоа задржувајќи читлива граница меѓу нарачките и клиентите. Сепак, треба да се потврди со логови на барања и податоци слични на продукциските. Индекс што помага на развојна база на податоци со неколку илјади редови можеби нема да одговара на шемата на пристап на поголема табела, а навидум едноставен филтер може да стане скап кога спречува користење индекс.

И трансакциите третирајте ги како прашање на перформанси. Држете ги тесни, избегнувајте мрежни повици додека држите заклучувања и направете го однесувањето при повторување експлицитно таму каде што се очекуваат привремени конфликти. Повторен обид без идемпотентна операција може да претвори привремен проблем со базата на податоци во дуплирана работа или дупликат записи.

Направете ги оддалечените повици видливи и ограничени

Секој HTTP-повик, пребарување во кеш, објавување порака и операција со датотека воведува зависност од нешто надвор од тековниот PHP-процес. На тие повици им требаат временски ограничувања, справување со неуспеси и одлука за одговорност: дали ова барање треба да чека, да врати делумни информации, да користи кеширани податоци или да ја предаде работата на редица?

Недостасувачкото временско ограничување не е трпение; тоа е неограничено зафаќање на капацитетот на работниците. Ако доволно барања чекаат на деградирана надворешна услуга, PHP-FPM работниците или работниците на апликацијата можат да се пополнат. Здравите барања потоа чекаат зад нездравите, а локализиран проблем со зависноста станува широк прекин.

Поставете ги намерно временските ограничувања за поврзување и за целото барање, разликувајте неуспеси што може повторно да се обидат од трајните и ограничете ги повторните обиди. Повторните обиди треба да користат backoff и да се применуваат само кога операцијата е безбедна за повторување. На пример, повторување на читање може да биде разумно; повторување на создавање плаќање бара механизам за идемпотентност што системот-примач го почитува.

Преместете ја неитната работа надвор од критичната патека

Многу барања не мора да го извршат секој несакан ефект пред да одговорат. Испраќањето известувања, генерирањето извештаи, синхронизирањето индекси за пребарување и обработката на прикачувања често е подобро да се ракува асинхроно. Ова ја подобрува перципираната латентност, но го менува дизајнот: на задачите им требаат трајно складирање, набљудливост, политики за повторни обиди и јасно ракување со работа што постојано не успева.

Редица не е магија за перформанси. Таа го заменува непосредното време на одговор со евентуално завршување и оперативна одговорност. Користете ја каде што тој компромис е прифатлив и изложете доволно статус за одложената работа да биде разбирлива за корисниците и тимовите за поддршка.

Кеширајте го резултатот од скапите одлуки

Кеширањето функционира најдобро кога е поврзано со јасен трошок и јасна стратегија за поништување. Кеширајте скап агрегат, често барано референтно множество податоци или рендериран фрагмент што ретко се менува. Не додавајте кеш само затоа што крајната точка делува бавно; прво утврдете дали скапата работа е доволно стабилна за повторна употреба.

Клучевите на кешот треба да го вклучуваат секој влез што суштински го менува резултатот, како локалитет, опсег на авторизација, филтри или верзија. Премногу широк клуч предизвикува промашувања. Премногу тесен клуч може да ги изложи податоците на еден корисник на друг или да врати неточна содржина.

Поништувањето е тестот на дизајнот. Ако податоците се променат, одлучете дали веднаш да го поништите кешот, да ја ажурирате кешираната вредност, да користите краток рок на траење или да прифатите ограничена застареност. Нема универзален одговор, но треба да постои експлицитен. За критична точност, претпочитајте ја поедноставната патека дури и ако е помалку агресивна.

Капацитетот е дел од дизајнот на апликацијата

PHP-апликациите најчесто работат зад менаџер на процеси, платформа за контејнери или и двете. Бројот на работници, ограничувањата на меморијата, конекциите со базата на податоци и ограничувањата на ресурсите на контејнерите сочинуваат еден систем. Зголемувањето на PHP-работниците може да ја подобри конкурентноста само ако и низводните зависности имаат капацитет. Во спротивно, може да создаде повеќе конкуренција за базата на податоци, повеќе истовремени оддалечени повици и поголема потрошувачка на меморија.

Во распоредувања засновани на Docker, држете ја конфигурацијата надворешна и направете ја подготвеноста значајна. Стартувањето на контејнер не гарантира дека неговата апликација може безбедно да опслужува сообраќај. Проверките при распоредување треба да го одразуваат она што навистина ѝ е потребно на услугата, притоа избегнувајќи кршливи проверки што го рестартираат инаку здравите процеси при секое запнување на зависност.

Тестирањето на оптоварување е највредно кога наликува на реалноста: репрезентативни големини на товар, автентицирани патеки, загреан кеш, истовремени корисници и побавни зависности. Тестирајте ги патеките на неуспех, како и патеките на успех. Систем што работи добро само кога секоја услуга одговара моментално не е тестиран во своите најважни услови.

Перформансите се дисциплина на одржување

Трајната добивка не е еднократна оптимизација. Таа е правење на регресиите во перформансите полесни за откривање и на скапото однесување потешко за воведување. Држете го бројот на барања видлив во развојот и тестовите, каде што е практично. Прегледувајте јамки што вршат I/O. Додајте контролни табли за важните крајни точки и редици. Поставете предупредувања за симптомите што ги чувствуваат корисниците, а не само за инфраструктурската активност.

Добрите PHP-перформанси се смирен системски дизајн: помалку непотребна работа, помалку неконтролирани чекања, предвидливо однесување при неуспех и одлуки за капацитет засновани на докази. Бенчмарковите можат да укажат на можности. Здравјето на системот во реалниот свет е она што докажува дека апликацијата останува брза кога нејзините податоци растат, зависностите се однесуваат лошо и корисниците пристигнуваат во исто време.

Портрет на автор на блогот

Mihajlo

Јас сум Михајло - развивач поттикнат од љубопитност, дисциплина и постојаната желба да создадам нешто значајно. Споделувам увиди, упатства и бесплатни услуги за да им помогнам на другите да ја поедностават својата работа и да растат во постојано развивачкиот свет на софтверот и вештачката интелигенција.