ИТ развој

Rethink Your Database Architecture: The Unsung Hero of Backend Performance

Преиспитајте ја архитектурата на вашата база на податоци: неопеаниот херој на перформансите на бекендот

Многу проблеми со перформансите на бекендот пристигнуваат маскирани како проблеми во апликацијата. Бавен краен пристап добива уште еден слој кеш. На барање на контролна табла му се доделува подолго временско ограничување. PHP пул од работници се подесува сè додека не стане скап и кревок. Понекогаш тие промени помагаат. Често, тие само го кријат вистинското ограничување: архитектурата на базата на податоци повеќе не одговара на начинот на кој се користи системот.

Дизајнот на базите на податоци ретко е гламурозен. Не создава драматична демонстрација и можеби не се појавува во патоказот на производот. Сепак, тој одредува колку сигурно одговара API, колку безбедно тимот може да испорачува промени и дали апликацијата останува разбирлива додека растат податоците и сообраќајот. Третирањето како имплементациски детаљ е еден од најсигурните начини да се создаде бекенд што изгледа брз во развој, а непредвидлив во продукција.

Перформансите почнуваат од обликот на податоците

Пред да го оптимизирате кодот, поставете поедноставно прашање: кои податоци всушност му се потребни на ова барање и како се складирани тие податоци? Одговорот влијае на секој слој над нив.

Разгледајте API краен пристап што враќа нарачка со нејзиниот клиент, ставки, плаќања и статус на испорака. Наивна ORM имплементација може тој навидум едноставен одговор да го претвори во низа барања: едно за нарачката, едно за клиентот, а потоа по едно барање за секоја поврзана колекција или запис. Ова е познатиот проблем со N+1 барања, но пошироката поука е поважна: погодноста на ниво на апликација може да прикрие работа на ниво на база на податоци.

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

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

Индексите се одлуки за производот, не само поставки на базата на податоци

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

Да претпоставиме дека API најчесто ги презема неодамнешните платени фактури за една сметка:

SELECT id, total, issued_at
FROM invoices
WHERE account_id = ?
  AND status = 'paid'
ORDER BY issued_at DESC
LIMIT 50;

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

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

Одржувајте ги трансакциите кратки, а границите јасни

Трансакциите ја штитат конзистентноста, но не се бесплатни. Долготрајните трансакции можат да држат заклучувања, да задржуваат стари верзии на редови, да ја зголемат конкуренцијата и да го отежнат закрепнувањето од неуспеси. Ова е особено лесно да се превиди во PHP апликации, каде што едно барање може да изврши работа со база на податоци, да повика надворешен API, да исцрта одговор и да испрати известување во еден линеарен тек.

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

$pdo->beginTransaction();

try {
    $orderId = createOrder($pdo, $payload);
    addOutboxEvent($pdo, 'order.created', ['order_id' => $orderId]);
    $pdo->commit();
} catch (Throwable $e) {
    $pdo->rollBack();
    throw $e;
}

Овој образец не ја елиминира сложеноста; тој ја поставува сложеноста таму каде што може намерно да се управува со неа. Работник може повторно да се обиде да го достави outbox настанот. Барањето може да врати одговор откако трајната состојба е потврдена. Ако дојде до повторен обид, потрошувачите треба да бидат идемпотентни, што значи дека истиот настан може да се обработи повеќе од еднаш без да создаде дупликатни деловни последици.

Одвојте ги оперативните барања од аналитичкиот апетит

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

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

Во зависност од системот, тоа може да значи реплика за читање, база на податоци за известување освежувана преку контролирано преместување на податоци, претходно пресметани агрегации или асинхрон процес за извоз. Соодветниот избор зависи од барањата за конзистентност. Екран за финансии можеби има потреба од точна трансакциска вистина; управувачка контролна табла може да прифати кратко доцнење во замена за стабилни перформанси во продукција.

Направете ја конзистентноста свесен договор

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

Дизајнирајте API-и за да избегнете случајна работа со базата на податоци

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

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

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

Docker треба да го олесни репродуцирањето на однесувањето на базата на податоци

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

Одржувајте ги промените на шемата верзионирани и распоредувајте ги внимателно. Адитивните промени генерално се побезбедни: создадете колона што дозволува null, дополнете ги податоците во контролирани серии ако е потребно, ажурирајте го кодот на апликацијата, па потоа заострете ги ограничувањата откако стариот код повеќе не ја користи претходната структура. Деструктивните промени заслужуваат дополнително внимание бидејќи постепеното распоредување може привремено да извршува повеќе верзии на апликацијата со истата шема.

Резервните копии и тестовите за враќање исто така припаѓаат во овој разговор. Резервна копија што никогаш не била вратена е претпоставка, а не план за опоравување. Архитектурата ја вклучува и оперативната патека за справување со грешки, оштетени податоци и неуспешни распоредувања.

Трајната предност е јасноста

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

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

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

Mihajlo

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