Надвор од кеширањето: Архитектирање бази на податоци за кориснички патувања без доцнење
Кеширањето често е првата алатка за перформанси кон која посегнуваат тимовите. Тој инстинкт е разбирлив: додадете кеш, гледајте како бавниот крајна точка станува побрза и продолжете понатаму. Но кешот може само да ја скрие латентноста што веќе постои. Не може да поправи корисничко патување чиј модел на податоци, облици на пребарувања, API-граници и избори за распоредување го принудуваат системот да врши непотребна работа.
Без латентност не значи дека секое барање завршува веднаш. Значи дека корисникот ретко чека работа што можела да се избегне, одложи, претходно пресмета или отстрани од критичната патека со дизајнот. Тоа е архитектонска цел, а не Redis-конфигурација.
Почнете со патувањето, не со базата на податоци
Пребарувањето на база на податоци никогаш не е бавно изолирано. Бавно е во однос на корисничко дејство. „Отвори контролна табла“, „додај ставка во кошничка“ и „види статус на нарачка“ имаат различни очекувања, толеранции на неуспех и барања за конзистентност.
Мапирајте ја патеката на барањето пред да ги прилагодувате поединечните пребарувања. За PHP API, таа патека може да вклучува автентикација, валидација на барањето, апликациски услуги, ORM-хидратација, читања од база на податоци, повици до друга услуга, серијализација и одговор. Пребарување на база на податоци од 250 ms има различно значење кога е единствената операција отколку кога се наоѓа зад три последователни мрежни повици.
Поставете директно прашање за секоја интеракција: кои информации мора да бидат тековни пред одговорот да биде корисен? Одговорот често ја намалува работата на синхроната патека. Контролната табла може да има потреба од тековни дозволи за сметката, но може да прикажува малку задоцнети збирни броеви. Страницата за потврда на нарачка има потреба од траен запис за нарачката, но потврдата по е-пошта не мора да биде испратена пред прелистувачот да добие успех.
Дизајнирајте за пребарувањата што навистина ги опслужувате
Дизајнот на шемата започнува со исправност, но и обрасците на читање мора да ја обликуваат. Нормализираниот модел често е правилниот извор на вистина. Од тоа не следи дека секој екран треба да го реконструира својот приказ преку длабок синџир на спојувања и пресметки по ред.
Идентификувајте ги пребарувањата со висока фреквенција и направете ги експлицитни. За секое, дефинирајте ги филтрите, редоследот на сортирање, очекуваната големина на резултатот и потребните колони. Потоа проверете дали базата на податоци може ефикасно да го изврши тој образец на пристап со соодветен индекс. Индексот не е декорација додадена по инцидент; тој е структура на податоци избрана за познато прашање.
На пример, неодамнешните фактури на една сметка може да се бараат по сметка, да се подредуваат според времето на создавање и да се страницираат. Композитен индекс усогласен со тој образец обично е повреден од независно индексирање на секоја колона:
CREATE INDEX invoices_account_created_at_idx
ON invoices (account_id, created_at DESC);
Прецизната синтакса и ефикасност зависат од моторот на базата на податоци и пребарувањето, затоа проверете го планот за извршување во средина што наликува на продукциската. Исто така, избирајте ги само полињата што ѝ се потребни на крајната точка. Преземањето големи текстуални колони или хидратирањето сложени графови на објекти за компактен одговор со листа истовремено ја зголемува работата на базата на податоци, PHP и мрежата.
Направете ја пагинацијата предвидлива
Пагинацијата со offset станува сè поскапа кај големи, често менувани множества на податоци. Пагинацијата базирана на курсор може подобро да одговара за хронолошки доводи и ревизорски траги бидејќи продолжува од позната граница, наместо да бара од базата на податоци да прескокне сè поголем број редови.
SELECT id, created_at, status
FROM invoices
WHERE account_id = :account_id
AND created_at < :cursor_created_at
ORDER BY created_at DESC
LIMIT :page_size;
Вистинскиот курсор исто така има потреба од стабилен разрешувач на изедначувања кога временските ознаки може да се совпаѓаат. Важниот принцип е стабилно подредување: корисниците не треба да гледаат дупликати или записи што недостигаат додека се движат низ страниците.
Одделете ги запишувањата од скапите модели за читање
Не секоја крајна точка треба директно да го пребарува моделот за запишување. Кога на екранот му требаат вкупни износи, резимеа на статуси или податоци составени од неколку ентитети, модел за читање изграден за намената може повторуваните пресметки да ги претвори во едноставно пребарување.
Ова не бара голем редизајн воден од настани. Прагматичен пристап може да одржува табела account_dashboard кога се менува состојбата на фактурата или плаќањето. Барањето тогаш чита еден компактен запис наместо повторно да пресметува агрегати низ неколку табели.
Компромисот е експлицитен: моделите за читање воведуваат логика за ажурирање и може накратко да бидат застарени. Тоа е прифатливо само кога патувањето го дозволува тоа. Направете ја свежината видлива во дизајнот. Користете ја трансакциската патека за запишување за дејства што бараат непосредна конзистентност, а асинхроно одржувани проекции таму каде што мало доцнење е безопасно.
- Јасно одржувајте го авторитативниот запис.
- Направете ги ажурирањата на проекциите идемпотентни за повторните обиди да бидат безбедни.
- Прикажувајте значајни состојби како што се чекање или обработка, наместо да се преправате дека асинхроната работа е завршена.
- Испланирајте како повторно да изградите проекција ако нејзината логика се промени.
Држете ја бавната работа надвор од патеката на барањето
Многу проблеми со латентноста всушност се проблеми со работниот тек. Генерирањето извештај, менувањето големина на отпремање, испраќањето известувања, пресметувањето препораки или синхронизирањето со трета страна ретко треба да блокира веб-барање.
Во PHP апликација, зачувајте ја намерата на корисникот и ставете ја дополнителната работа во ред преку сигурен механизам соодветен за системот. Одговорот може да го потврди прифатеното дејство, додека работникот го обработува некритичниот чекор. Работникот мора да претпостави дека испораката може да се случи повеќе од еднаш и дека надворешните повици може да не успеат по истек на времето, дури и ако оддалечениот систем ја завршил работата.
Тоа значи дека идемпотентноста не е опционална. Дајте им на надворешно значајните операции клуч за идемпотентност, зачувајте ја состојбата на обработка, користете ограничени повторни обиди со backoff и испратете ги неуспесите на патека што може да се прегледа откако повторните обиди ќе се исцрпат. Избегнувајте циклуси на повторни обиди во рамките на едно HTTP-барање; тие трошат PHP-работници токму кога зависноста е нездрава.
Третирајте ги API-границите како граници на латентност
Чистата внатрешна граница на услуга сè уште може да создаде лошо корисничко искуство ако едно барање од прелистувачот активира синџир од синхрони повици кон услуги. Секој скок додава трошок за поврзување, чекање во ред, серијализација и уште едно место за истек на времето.
Претпочитајте API-ја што враќаат корисен одговор во облик на екран кога тоа ги намалува кружните патувања без да создаде неуправлива крајна точка „сè“. Групирајте независни пребарувања каде што е практично. Намерно поставувајте временски ограничувања, разликувајте привремен неуспех на зависност од невалиден влез и одлучете дали делумен одговор е подобар од целосен неуспех.
За распоредувања засновани на Docker, перформансите зависат и од оперативните основи: врските со базата на податоци мора да бидат ограничени, паралелизмот на работниците мора да одговара на достапниот капацитет на базата на податоци, а контејнерите треба да имаат доволно време да ја завршат работата во тек при исклучување. Скалирањето на PHP-контејнери без контрола на пуловите за врски може едноставно да го премести тесното грло во базата на податоци.
Мерете ја целата патека
Инструментацијата треба да му овозможи на тимот да одговори каде отишло времето за конкретно барање. Запишувајте го времетраењето на барањето, бројот и времетраењето на пребарувањата во базата на податоци, времињата на надворешните зависности, староста на редицата, стапката на грешки и сигнали за заситеност како исцрпени работници или врски со база на податоци. Идентификаторите за корелација ги прават овие сигнали корисни низ API-барање и неговата асинхрона дополнителна работа.
Користете ги податоците за прво да го отстраните најголемиот повторуван трошок. Отстранувањето непотребно пребарување од секое барање често е подобро од намалување неколку милисекунди од редок позадински процес. Тестирањето под оптоварување треба да вклучува реалистичен паралелизам и обем на податоци; пребарување што е елегантно на мало развојно множество податоци подоцна може да стане оперативен товар.
Изградете ја патеката што корисниците ја чувствуваат
Кешовите остануваат вредни. Тие ги штитат скапите читања, апсорбираат нагли оптоварувања и го намалуваат притисокот врз примарното складиште. Но најдобро функционираат како еден слој во систем што веќе поставува разумни прашања, ја извршува критичната работа еднаш и оттурнува сè друго од патот.
Трајната навика за перформанси е едноставна: проследете го корисничкото патување, идентификувајте ја минималната синхрона вистина што му е потребна и изградете ја архитектурата на секој слој околу сигурното испорачување на таа вистина. Кога базата на податоци, API-то, редицата и моделот на распоредување заедно го поддржуваат тоа патување, брзината престанува да биде кеш-погодок и станува својство на дизајнот.