Перформанси на бази на податоци: Инженерство за предвидливо оптоварување
Перформансот на базата на податоци ретко откажува одеднаш. Почесто, постепено се влошува: на контролна табла ѝ треба малку повеќе време, задача во заднина се преклопува со врвниот сообраќај, навидум безопасен API филтер предизвикува бавно барање, а пуловите за конекции почнуваат да чекаат. Кога корисниците ќе забележат, проблемот веќе не е едно барање. Тој е интеракција меѓу обликот на податоците, шаблоните на пристап, конкуренцијата и оперативните ограничувања.
Целта не е секое барање изолирано да биде што е можно побрзо. Целта е системот да се однесува предвидливо при очекувано оптоварување и намерно да деградира кога побарувачката го надминува тоа оптоварување.
Почнете со работното оптоварување, а не со дијаграмот на шемата
Добро нормализирана шема сепак може да има лоши перформанси ако апликацијата ѝ поставува погрешни прашања. Пред да додадете индекси или да ја подесувате конфигурацијата, идентификувајте го работното оптоварување: кои барања се чести, кои се чувствителни на латентност, кои патеки за запишување меѓусебно се натпреваруваат и кои извештаи можат да се извршуваат асинхроно.
API крајна точка што вчитува еден клиент се разликува од административно пребарување низ милиони записи. Работен тек за плаќање може да бара непосредна конзистентност, додека аналитички екран може сосема прифатливо да има доцнење од неколку минути. Третирањето на сите читања и запишувања како подеднакво важни е начинот на кој системите трошат скапи ресурси на погрешна работа.
Корисни прашања вклучуваат:
- Кои се барањата со најголем обем?
- Кои операции се извршуваат за време на сообраќајните врвови?
- Колку редови испитува секое барање наспроти колку враќа?
- Кои крајни точки активираат повторени барања по барање?
- Кои работни оптоварувања можат да се преместат во редица, кеш, реплика или претходно пресметана табела?
Ова врамување ја претвора работата на перформансите од нагаѓање во приоритизација.
Направете го обликот на барањето видлив
Повеќето инциденти со базата на податоци стануваат полесни за дијагностицирање кога апликацијата може да поврзе бавно барање со шаблон на барање. Евидентирајте ги бавните барања со времетраење, испитани редови каде што е достапно, контекст на барањето и безбеден отпечаток на барањето. Избегнувајте евидентирање тајни или чувствителни податоци за клиентите, но зачувајте доволно структура за да препознаете повторување.
Потоа проверете ги плановите за извршување. Индексот не е автоматски корисен само затоа што постои; оптимизаторот мора да може да го користи за вистинскиот шаблон на предикат, спојување, сортирање и групирање. Барање што филтрира според account_id, сортира според created_at и ги ограничува резултатите често има корист од индекс што го одразува тој шаблон на пристап.
SELECT id, status, created_at
FROM orders
WHERE account_id = ?
AND status = ?
ORDER BY created_at DESC
LIMIT 50;
За ова барање, индекс што почнува со account_id и status, по кои следи created_at, може да поддржи и филтрирање и подредено преземање. Точниот избор сепак зависи од распределбата на податоците и механизмот на базата на податоци, затоа потврдете го со планот и реалистични податоци.
Индексите имаат цена. Секој дополнителен индекс троши складишен простор и ги прави вметнувањата, ажурирањата и бришењата поскапи. Индексите се дел од патеката за запишување, а не бесплатни прекинувачи за перформанси.
Спречете случајна работа во апликацискиот слој
Кодот на задниот дел често индиректно создава оптоварување на базата на податоци. Класичен пример е проблемот со N+1 барања: вчитувате листа на записи, а потоа издавате уште едно барање за поврзаните податоци на секој запис. Може да изгледа безопасно во развој со десет редови и да стане катастрофално кога вистинска сметка има илјадници.
Користете нетрпеливо вчитување, спојување или намерно сериско барање кога се потребни поврзаните податоци. Во исто време, не преземајте цел граф на објекти стандардно. Подобрата навика е да ги обликувате податоците за конкретниот случај на употреба: изберете само потребни колони, страницирајте големи колекции и избегнувајте да претворите база на податоци во машина за хидратација на објекти.
Пагинацијата заслужува посебно внимание. Пагинацијата со отстапување е погодна, но големите отстапувања може да бараат базата на податоци да скенира и отфрла сè поголема количина податоци. За големи, подредени множества податоци, пагинацијата со клучеви често е постабилна.
SELECT id, created_at, title
FROM articles
WHERE created_at < ?
ORDER BY created_at DESC
LIMIT 25;
Курсорот треба да се заснова на стабилно подредување, често со единствен разрешувач на изедначеност како id. Ова избегнува дупликати или недостигачки редови кога повеќе записи имаат ист временски печат.
Контролирајте ја конкуренцијата пред таа да ве контролира вас
Базата на податоци може да е здрава додека апликацијата ја преоптоварува со премногу истовремени барања. Отворањето нова конекција за секој PHP worker, потрошувач на редица, закажана задача и ad hoc команда може да ги исцрпи достапните конекции многу пред CPU или дискот да стане видливо тесно грло.
Намерно поставете ограничувања за конекциите низ целиот стек. Земете ги предвид веб-процесите, обработувачите на задачи, преклопувањето при распоредување, здравствените проверки и административните алатки. Пулот за конекции треба да обезбеди повратен притисок, наместо да стане механизам за криење на неограничена конкуренција.
Трансакциите исто така треба да бидат кратки и целисходни. Одржувањето отворена трансакција додека повикувате надворешен API, испраќате е-пошта или извршувате бавна апликациска работа го зголемува траењето на заклучувањето и конкуренцијата. Зачувајте ја состојбата што мора да биде атомска, потврдете ја трансакцијата, а потоа предадете ја нетрансакциската работа на редица или процес управуван од outbox.
Кога ажурирате спорни записи, намерно изберете модел на конкуренција. Оптимистичкото заклучување може добро да функционира кога конфликтите се ретки; заклучувањето на редови може да биде соодветно кога точноста бара сериски ажурирања. Важниот дел е неуспехот да се обработува како нормален исход. Застои и конфликти при серијализација можат да се појават дури и во правилно дизајнирани системи, затоа повторувајте само операции што е безбедно да се повторат и користете ограничен број обиди со одложување.
Кеширајте внимателно, а не рефлексно
Кеширањето е вредно кога отстранува повторена, скапа работа, но создава втор систем на вистина со сопствени начини на инвалидирање и откажување. Кеширајте стабилни референтни податоци, прикажани агрегати и скапи читања со јасен договор за свежина. Не користете кеш за да скриете неограничено барање или стратегија за пагинација што недостига.
Бидете експлицитни за тоа што се случува при промашување на кешот, прекин на кешот или stampede. Ако стотици барања истовремено ја обновуваат истата скапа вредност, кешот може да го засили проблемот со базата на податоци што требало да го реши. Техники како здружување на барања, краткотрајни заклучувања, однесување stale-while-revalidate и ограничувања на стапката можат да го одржат закрепнувањето предвидливо.
Дизајнирајте со оперативна резерва
Подесување на перформансите што успева само во тивка средина не е подесување. Тестирајте реалистична конкуренција, големини на податоци слични на продукциските и мешани работни оптоварувања. Во моделот вклучете распоредувања, повторни обиди, неуспешни повици кон зависни системи, бавни потрошувачи и долготрајни извештаи. Ова се нормални оперативни услови, а не гранични случаи.
Мерете мал сет значајни сигнали: латентност на барањата, латентност на базата на податоци, употреба на конекции, чекања на заклучувања, стапки на грешки, длабочина на редицата и индикатори за заситеност. Алармирајте за трајно влошување наместо за единечен бучен скок и одржувајте ги контролните табли поврзани со однесувањето видливо за корисниците.
Предвидливото оптоварување не е отсуство на притисок. Тоа е способност да се разбере каде оди притисокот, што го ограничува и како системот реагира кога ќе се достигнат тие ограничувања.
Најтрајната работа за перформансите на базата на податоци обично не е гламурозна: прецизен индекс, помал сет резултати, ограничен пул, пократка трансакција, извештај преместен во заднина и искрено ограничување на капацитетот. Заедно, овие избори ги претвораат перформансите од напор за спасување во доцна фаза во архитектонско својство на системот.