Преиспитајте го дизајнот на вашата база на податоци за нераскинливи перформанси на системот
Системот ретко откажува затоа што едно барање е бавно. Откажува затоа што дизајнот на базата на податоци тивко ја прави бавната патека неизбежна: секое барање спојува премногу податоци, секое ажурирање допира премногу редови и секоја нова функционалност додава уште еден исклучок во шема што повеќе не го одразува начинот на кој функционира производот.
Дизајнот на базата на податоци често се третира како рана проектна пресвртница. Во пракса, тој е одлука за перформанси што останува активна во текот на целиот животен век на системот. Табелите, клучевите, ограничувањата и шемите на пристап што ги избирате одредуваат колку лесно може да расте API, колку безбедно може да се менуваат податоците и колку предвидлива останува апликацијата под оптоварување.
Прво моделирајте го бизнисот, потоа моделирајте ги барањата
Чист модел на ентитети е неопходен, но не е доволен. Базата на податоци може совршено да го претставува бизнисот, а сепак да ги прави скапи вообичаените барања на апликацијата. Почнете со основните ентитети и нивните односи, па идентификувајте ги операциите што се случуваат најчесто и се најважни.
За систем за нарачки, важните прашања не се само „Што е нарачка?“ и „Што е ставка од нарачка?“ Тие се и:
- Како клиентот ги презема неодамнешните нарачки?
- Како администратор наоѓа нарачки што чекаат плаќање?
- Кои податоци се потребни за прикажување на API-одговор со детали за нарачка?
- Кои записи мора да се ажурираат заедно кога плаќањето ќе успее?
Овие прашања ги откриваат патеките што вашата шема мора да ги поддржува. Тие исто така спречуваат вообичаена грешка: дизајнирање табели изолирано и одложување на дизајнот на барањата додека апликацијата веќе не се изгради врз неефикасни претпоставки.
Нормализирајте за исправност, денормализирајте со докази
Нормализацијата останува една од најдобрите алатки за зачувување на интегритетот на податоците. Чувањето на е-поштенската адреса на клиентот еднаш, наместо нејзино копирање во секоја нарачка, избегнува спротивставени ажурирања. Одделувањето на ставките од нарачките спречува повторувачки групи и ги прави количините, цените и производите експлицитни.
Но строгата нормализација не е религија. Некои податоци треба намерно да се копираат кога претставуваат историски факт или отстрануваат скапа, добро разбрана патека за читање. Ставката од нарачка обично треба да го зачува името на производот и единечната цена во моментот на купувањето, дури и ако производот подоцна се промени. Тоа не е невнимателно дуплирање; тоа е запис за продаденото.
Клучната разлика е сопственоста и намерата. Дуплираните податоци се опасни кога повеќе локации тврдат дека се тековен извор на вистината. Тие се корисни кога една вредност е непроменлива снимка, изведен модел за читање или кеш со јасна стратегија за освежување.
Направете го изворот на вистината експлицитен
Секогаш кога податоците се појавуваат на повеќе од едно место, документирајте која вредност е меродавна и како се одржуваат останатите. Ако збирна табела содржи салдо на сметка, одлучете дали се ажурира во истата трансакција како записите во главната книга, се обновува асинхроно или се третира како проекција што може да се отфрли. Нејаснотијата тука подоцна станува продукциски грешки.
Индексите се дел од интерфејсот
Индексот не е незадолжителен слој за оптимизација додаден по објавувањето. Тој е дел од договорот меѓу кодот на апликацијата и складиштето. Ако крајна точка доследно филтрира по account_id и подредува според најновото време на создавање, шемата треба да ја направи таа патека природна.
SELECT id, status, created_at
FROM orders
WHERE account_id = ?
ORDER BY created_at DESC
LIMIT 50;
Композитен индекс како (account_id, created_at) често многу подобро одговара на оваа шема на пристап отколку посебни индекси за секоја колона. Редоследот е важен: водечките колони треба да се усогласат со начинот на кој барањето го стеснува множеството резултати.
Индексите имаат и цена. Секој дополнителен индекс троши простор за складирање и мора да се одржува при вметнувања, ажурирања и бришења. Индексирањето на секоја колона изгледа безбедно сè додека работните оптоварувања со многу запишувања не станат побавни, а одржувањето на шемата не стане нејасно. Додавајте индекси за да поддржите реални предикати, спојувања, подредување и правила за единственост — не затоа што некоја колона можеби еден ден ќе се пребарува.
Ограничувањата за единственост заслужуваат посебно внимание. Логиката на ниво на апликација „провери, па вметни“ е ранлива на истовремени барања. Ако е-поштенската адреса, надворешната референца за плаќање или клучот за идемпотентност мора да бидат единствени, наметнете го тој факт во базата на податоци.
Нека ограничувањата ја заштитат апликацијата
Валидацијата во заднината е суштинска за корисни пораки за грешка и ергономија на API. Таа не е замена за ограничувањата на базата на податоци. Барањата може да пристигнат преку скрипти, редици, администраторски алатки, увози или идни услуги што заобиколуваат една конкретна патека за валидација.
Користете ја базата на податоци за да наметнете инваријанти што секогаш мора да важат: задолжителни вредности, единствени идентитети, валидни односи со странски клучеви и разумни ограничувања на доменот каде што се поддржани. Странскиот клуч прави повеќе од зачувување на квалитетот на податоците; тој им кажува на идните одржувачи дека односот е значаен и не треба тивко да се наруши.
За промени во повеќе чекори, користете трансакции. Потврдата за плаќање што создава запис во главната книга, означува нарачка како платена и резервира залиха не треба да го остави само првиот чекор потврден ако третиот не успее.
$pdo->beginTransaction();
try {
$markOrderPaid->execute([$orderId]);
$createLedgerEntry->execute([$orderId, $amount]);
$reserveInventory->execute([$orderId]);
$pdo->commit();
} catch (Throwable $exception) {
if ($pdo->inTransaction()) {
$pdo->rollBack();
}
throw $exception;
}
Ова не го решава секој проблем со дистрибуираните системи. На пример, надворешен давател на плаќања не може да учествува во истата трансакција на базата на податоци. Во тие случаи, дизајнирајте за повторни обиди, идемпотентност и експлицитни премини на состојби наместо да се преправате дека мрежниот повик е атомски.
Дизајнирајте API-ја околу ограничен пристап до податоци
Многу проблеми со базата на податоци потекнуваат од API-слој што дозволува неограничено однесување. Враќањето на секој соодветен запис, вчитувањето вгнездени односи по еден ред или прифаќањето произволни полиња за подредување може да го претвори вообичаениот сообраќај во инцидент со базата на податоци.
Вградете ограничувања во договорот на API. Страничете ги колекциите, наведете дозволени полиња за подредување, ограничете ја големината на страниците и преземајте ги поврзаните записи во намерни групи. Познатиот проблем со барањата N+1 не е само непријатност на ORM; тој е несовпаѓање меѓу обликот на одговорот и планот за пристап до податоци.
За листа на нарачки, прво преземете ја страницата со нарачки, а потоа вчитајте ги сите потребни ставки со барање ограничено на тие идентификатори на нарачки. Алтернативно, користете внимателно измерено спојување или посветен модел за читање кога одговорот е стабилен и често се користи. Правилниот избор зависи од големината на резултатот, однесувањето на базата на податоци и трошокот за составување податоци во PHP.
Планирајте ги промените на шемата како продукциски операции
Миграцијата е код што менува живи структури на податоци. Третирајте ја со истото внимание како распоредување што го менува однесувањето на апликацијата. Додавањето nullable колона обично е полесно отколку веднаш да ја направите задолжителна. Замената на колона често функционира подобро како низа: додајте ја новата структура, запишувајте ги двете вредности ако е потребно, безбедно пополнете ги постојните податоци, префрлете ги читачите, па отстранете ја старата структура по проверка.
Премините со повратна компатибилност се важни кога инстанците на апликацијата се распоредуваат постепено или работниците во заднина извршуваат постар код. Миграција што претпоставува дека секој процес се менува одеднаш може да создаде откажувања што се појавуваат само при реалниот временски распоред на распоредувањето.
Одржувајте ги миграциите мали, обратливи каде што е практично и јасни во однос на ефектите врз податоците. Тестирајте ги со репрезентативни обеми на податоци кога е можно. Наредба што е безопасна во локална Docker база на податоци може да задржи заклучувања или да работи многу подолго во продукција.
Мерете ја патеката, не претпоставката
Плановите за извршување на барања, дневниците за бавни барања, трагите на барања и метриките на базата на податоци се покорисни од интуицијата. Пред да промените барање, идентификувајте го вистинското работно оптоварување, прегледајте го планот за извршување и потврдете дали индексот се користи како што се очекува. По промената, измерете повторно.
Работата на перформанси е најтрајна кога истовремено ја подобрува и јасноста: построг договор на API, значајно ограничување, индекс што документира примарно пребарување или трансакција што ја прави бизнис-операцијата експлицитна.
Најотпорните дизајни на бази на податоци не се најразработените. Тие го прават исправното однесување лесно, скапото однесување тешко, а идните промени разбирливи. Кога шемата ги одразува и доменот и начинот на кој системот навистина се користи, перформансите престануваат да бидат спасувачка мисија и стануваат својство на архитектурата.