ИТ развој

Unlock Your Database's Potential: Beyond Indexing for Real Performance Gains

Отклучете го потенцијалот на вашата база на податоци: Надвор од индексирањето за вистински подобрувања во перформансите

Кога ќе се појави бавен endpoint, првиот инстинкт често е: „Додај индекс.“ Понекогаш тоа е токму правилно. Почесто, тоа е почетокот на истрагата, а не нејзиниот крај.

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

Почнете со вистинското тесно грло

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

Пред да ја менувате шемата или кодот, направете ја бавната патека набљудлива. Забележете го endpoint-от, обемот на барања, времетраењето, бројот на барања и репрезентативните параметри на барањата. Потоа проверете го планот на барањето за вистинското барање што се извршува. Важното прашање не е „дали оваа колона има индекс?“ Туку: „каква работа извршува базата на податоци и зошто?“

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

Направете го барањето помало пред да го направите побрзо

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

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

SELECT
    c.id,
    c.name,
    MAX(o.created_at) AS latest_order_at,
    COUNT(o.id) AS order_count
FROM customers AS c
LEFT JOIN orders AS o ON o.customer_id = c.id
WHERE c.account_id = ?
GROUP BY c.id, c.name
ORDER BY latest_order_at DESC
LIMIT 50;

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

Бидете подеднакво сомничави кон SELECT *. Тоа поврзува endpoint со секоја сегашна и идна колона, ги зголемува трошоците за пренос и може случајно да повлече големи текстуални или JSON полиња во често користени патеки. Изберете ги колоните што му се потребни на одговорот. Ова ги подобрува перформансите, ги прави API договорите појасни и ја намалува веројатноста невино додавање во шемата да стане скапо читање.

Пагинацијата е одлука за перформанси

Пагинацијата со offset е лесна за објаснување, што ја прави примамлива:

SELECT id, created_at, title
FROM posts
WHERE author_id = ?
ORDER BY created_at DESC, id DESC
LIMIT 25 OFFSET 5000;

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

Пагинацијата со курсор, или keyset, ѝ дава на базата на податоци покорисно место од кое да продолжи:

SELECT id, created_at, title
FROM posts
WHERE author_id = ?
  AND (created_at, id) < (?, ?)
ORDER BY created_at DESC, id DESC
LIMIT 25;

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

Индексите мора да одговараат на шемите на пристап

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

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

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

Индекс обликуван околу account_id, status и issued_at може добро да му служи на ова барање, зависно од механизмот на базата на податоци и распределбата на вредностите. Но не претворајте го тоа набљудување во правило дека секоја колона за филтрирање припаѓа во секој индекс. Полињата со ниска селективност, табелите со многу запишувања и конкурентните шеми на барања бараат расудување.

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

Запрете го случајното умножување на барања

Едно бавно барање е видливо. Сто умерени барања може да бидат полоши.

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

Поправете го ова намерно. Користете соодветно спојување, групно вчитајте ги поврзаните записи или конфигурирајте eager loading со ограничена листа на полиња. Потоа проверете го бројот на барања на ниво на барање. Избегнувајте да го замените N+1 со огромно eager loading што ја презема секоја вгнездена релација; вчитувајте само релации и колони што се потребни за тој одговор.

Користете кеширање како договор, а не како фластер

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

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

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

Перформансите ја вклучуваат и околината за распоредување

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

Поставете ограничувања за конкурентност и пулови имајќи го предвид капацитетот на базата на податоци. Заедно следете ја употребата на конекции, бавните барања, чекањата за заклучување, однесувањето на кешот, CPU, меморијата и латентноста на дискот. Во средини засновани на Docker, исто така направете ги ограничувањата на ресурси експлицитни и осигурете се постојаното складиште на базата на податоци да е соодветно за оптоварувањето. Контејнерите ја подобруваат конзистентноста на пакувањето; не ги отстрануваат латентноста на складиштето или притисокот врз меморијата.

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

Изградете навика за мерено подобрување

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

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

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

Mihajlo

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