Разјаснување на перформансите на базите на податоци: Надвор од профилирањето, кон вистински решенија
Бавната база на податоци ретко е само проблем на базата. Обично станува збор за системски проблем со маска во облик на база на податоци: API крајна точка презема премногу податоци, ORM тивко извршува десетици пребарувања, индексот повеќе не одговара на образецот на пристап или работник од редица се натпреварува со веб-сообраќајот за истите ресурси.
Профилирањето е суштинско бидејќи ви кажува каде се троши времето. Но профилот е почетна точка, а не решение. Вистинската работа е бавното пребарување или преоптоварениот пул на конекции да се претвори во промена што го подобрува однесувањето на целиот систем без да го отежне неговото одржување.
Почнете од барањето, а не од пребарувањето
Кога некоја крајна точка е бавна, примамливо е да ја отворите конзолата на базата на податоци и да ја оптимизирате најскапата SQL-наредба. Прво поставете пошироко прашање: што всушност треба да постигне ова барање?
Листата на производи може да прикажува само име, цена и минијатура, а сепак апликацијата може да вчитува целосни записи за производите, поврзан инвентар, секоја слика и неколку пресметани релации. Дури и добро индексирано пребарување е расипничко кога презема податоци што ќе бидат отфрлени.
Дефинирајте го обликот на одговорот пред да го подесувате SQL. Потоа усогласете го пристапот до податоци со него. Ова често го дава најбезбедното подобрување на перформансите бидејќи ја намалува работата, наместо само да ја забрзува непотребната работа.
$products = $repository->findVisibleProducts(
categoryId: $categoryId,
fields: ['id', 'name', 'price', 'thumbnail_url'],
limit: 24
);
Важен не е називот на методот. Важна е дисциплината: преземајте го најмалиот корисен сет на резултати, намерно применувајте страници и избегнувајте да ги третирате редовите од базата како неограничена колекција во меморијата.
Читајте ги плановите на пребарувања како објаснувања
Времетраењето на пребарувањето ви кажува дека нешто е бавно. Планот на пребарувањето помага да се објасни зошто. Користете ја алатката на вашата база за преглед на планови, како EXPLAIN, со репрезентативни пребарувања и реалистични вредности на параметрите. Барајте операции што укажуваат на несразмерна работа: целосни скенирања на големи табели, скапи сортирања, големи меѓурезултатни множества или спојувања што неочекувано ги умножуваат редовите.
Индексот е корисен само кога го поддржува вистинскиот образец на предикат, спојување и подредување. Реактивното додавање индекси може да ги забави запишувањата и да го прикрие вистинскиот образец на пристап. Наместо тоа, почнете со конкретно пребарување:
SELECT id, created_at, total
FROM orders
WHERE customer_id = ?
AND status = ?
ORDER BY created_at DESC
LIMIT 20;
Композитен индекс што започнува со customer_id и status, по кои следи created_at, може да се усогласи со ова пребарување. Но треба да се потврди во однос на планот на базата и стапката на запишување на оптоварувањето. Дизајнот на индекси не е контролна листа; тој е компромис меѓу побрзи читања, складирање и трошок за запишување.
Внимавајте на пребарувања што ги поништуваат добрите индекси
Некои изрази ја отежнуваат ефикасната употреба на колона во предикат. На пример, филтрирањето со функција применета на индексиран временски печат може да наметне повеќе работа отколку ограничен опсег.
SELECT id
FROM events
WHERE created_at >= ?
AND created_at < ?;
Оваа форма генерално е полесна за разбирање отколку трансформирањето на created_at во условот. Принципот е едноставен: зачувајте директна споредба меѓу зачуваната вредност и зададената граница секогаш кога деловното правило го дозволува тоа.
Елиминирајте го умножувањето на пребарувања
Класичниот проблем со N+1 пребарувања останува чест бидејќи се крие зад уреден код на апликацијата. Страница вчитува дваесет нарачки, а потоа секоја нарачка посебно го вчитува својот клиент или ставки. Поединечните пребарувања може да се брзи, но мрежните повратни патувања, употребата на конекции и повтореното планирање се насобираат.
Решете го со вчитување поврзани податоци во ограничени множества, со спојување кога добиениот облик на редови е соодветен или со пребарување на децата со услов IN и нивно групирање во кодот на апликацијата. Која опција е најдобра зависи од кардиналноста. Спојувањето на нарачки со мала релација клиент еден-на-еден е различно од спојувањето на нарачки со илјадници ставки.
- Користете нетрпеливо вчитување кога поврзаните податоци сигурно се потребни.
- Користете посебно сериско пребарување кога спојувањето би создало прекумерно дуплирани родителски редови.
- Користете експлицитни проекции за нетрпеливото вчитување да не стане нетрпеливо преземање на премногу податоци.
- Мерете го бројот на пребарувања по барање, како и вкупното времетраење.
ORM-ите се вредни, но не можат да ги заклучат карактеристиките на перформансите на деловен екран. Третирајте го генерираниот SQL како продукциски код: проверувајте го кога се менува однесувањето, особено околу релациите, филтрите и страниците.
Одделете ја трансакциската работа од сè останато
Трансакциите во базата на податоци треба да заштитуваат мала, кохерентна деловна промена. Тие не треба да вклучуваат HTTP-повици, бавна обработка на датотеки, испорака на е-пошта или долги пресметки. Одржувањето трансакција отворена додека чекате надворешна работа го зголемува времетраењето на заклучувањето и ја прави конкуренцијата поверојатна.
Практична граница често е: валидирајте го внесот, извршете ги потребните промени во базата, потврдете ја трансакцијата, а потоа испратете ја дополнителната работа во редица. Задачата во редицата треба да биде дизајнирана да поднесува повторни обиди. Тоа значи дека мора да избегнува создавање дупликати на несакани ефекти кога се извршува повеќе од еднаш.
На пример, задача што испраќа фактура треба да запише или провери трајна деловна состојба пред фактурата да ја третира како новоиспратена. Механизмот за повторни обиди не е гаранција за извршување точно еднаш; тој е причина секоја операција да биде безбедно повторлива, каде што е можно.
Пуловите на конекции, контејнерите и капацитетот се дел од дизајнот
Docker го олеснува извршувањето на многу контејнери на апликацијата. Исто така го олеснува создавањето повеќе конекции со базата отколку што базата може удобно да поднесе. Ако секој PHP работник може да отвора конекции и распоредувањето се скалира хоризонтално, вкупниот можен број конекции може да расте побрзо од очекуваното.
Поставете ги бројот на работници на апликацијата, ограничувањата на конекции и капацитетот на базата како еден систем. Оставете резерва за миграции, административен пристап, работници во заднина и привремени скокови на сообраќај. Кога конекциите се исцрпени, додавањето уште еден контејнер на апликацијата обично го засилува неуспехот наместо да го реши.
Исто така, разликувајте латентност од заситеност. Едно лошо индексирано пребарување може да предизвика повремени бавни барања. Заситена база може да предизвика широко распространети истекувања на време, заостанување во редицата и бури од повторни обиди. Решенијата се разликуваат, па набљудливоста треба да вклучува времетраење на пребарувања, број на пребарувања, активни конекции, стапки на грешки и длабочина на редицата.
Кеширајте го резултатот од одлука, а не збунетоста
Кеширањето може да биде моќно кога некоја вредност е скапа за пресметување, се бара повторно и е прифатливо да се испорача малку застарена. Многу помалку е корисно кога се додава пред да се разберат моделот на податоци и образецот на пребарување.
Изберете клучеви за кешот што ги одразуваат вистинските влезови, дефинирајте однесување за истекување и невалидирање и одлучете што се случува при промашување или прекин на кешот. Кеш што не може грациозно да откаже станува уште еден извор на проблеми со достапноста. За податоци што често се менуваат, размислете дали поедноставно пребарување или претходно пресметан модел за читање е полесен за управување од сложени правила за невалидирање.
Направете ги перформансите својство на архитектурата
Најсилните оптимизации на базата на податоци често се негламурозни: потесен API-одговор, индекс оправдан со вистински план, едно сериско пребарување наместо многу, пократка трансакција или ограничување при распоредувањето што спречува исцрпување на конекции.
Профилирањето укажува на жешката патека. Инженерската проценка одредува дали трајното решение припаѓа во SQL, PHP, API-договор, редица, кеш или планирање на капацитетот. Кога тимовите доследно прашуваат каква работа врши системот и зошто, перформансите престануваат да бидат итна вежба и стануваат намерен дел од дизајн што лесно се одржува.