Совладување со миграциите на бази на податоци за скалабилни бекенд архитектури
Миграциите на базата на податоци се местото каде што дизајнот на апликацијата се среќава со реалноста на продукцијата. Промената на шемата може да изгледа безопасно во pull request, а сепак да стане причината поради која распоредувањето застојува, API-то враќа грешки или зафатена база на податоци поминува минути држејќи заклучување. Разликата ретко е во самата алатка за миграции. Таа е во дисциплината што се користи за дизајнирање, секвенционирање, тестирање и распоредување на промената.
За скалабилен backend, миграциите треба да се третираат како верзиониран оперативен код. Тие дефинираат како перзистентната состојба се развива низ средини и мора да функционираат за празна база на податоци, долготрајна продукциска база на податоци и распоредување каде што старите и новите верзии на апликацијата накратко работат една до друга.
Сметајте ја шемата за јавен договор
Backend тимовите често применуваат размислување за компатибилност кај API-ја, но забораваат да го применат кај шемите на базите на податоци. Колона на табела, индекс, ограничување или вредност слична на enum може да ја користат повеќе верзии на апликацијата, позадински работници, извештаи, интеграции и оперативни скрипти.
Ова ги прави деструктивните промени особено ризични. Отстранувањето колона веднаш по промената на кодот на апликацијата претпоставува дека секој корисник веќе престанал да ја користи. Во дистрибуиран систем, таа претпоставка често е погрешна.
Побезбеден пристап е шаблонот прошири-и-стегни:
- Додајте ја новата структура на шемата без да ја отстраните старата.
- Распоредете код на апликација што може да ги чита или запишува двете форми каде што е потребно.
- Пополнете ги постојните податоци наназад во контролирани серии.
- Префрлете ги читањата и запишувањата на новата структура.
- Потврдете дека старата структура повеќе не се користи.
- Отстранете ја старата структура во подоцнежна миграција.
На пример, промената на единственото поле full_name на корисникот во first_name и last_name не е една атомска одлука за производот. Таа претставува неколку компатибилни промени во базата на податоци и апликацијата. Раздвојувањето на тие промени го одржува распоредувањето повратно на ниво на апликација и ја намалува веројатноста за делумно мигрирани податоци.
Направете ја секоја миграција детерминистичка
Миграцијата треба да ја произведе истата предвидена состојба на шемата секогаш кога се применува на поддржана почетна состојба. Тоа значи избегнување рачни измени само во продукција, недокументирани чекори за поставување и миграции што зависат од постоење податоци во одредена форма без да го проверат тоа.
Користете конвенција за именување што ги соопштува редоследот и намерата. Име на датотека како 20261102_add_order_status_index е покорисно од update_orders. Целта не е естетика; таа е историјата на распоредувањето да биде разбирлива кога ќе се случи инцидент месеци подоцна.
Одржувајте ги миграциите мали и фокусирани. Миграција што создава табели, трансформира милиони редови, обновува индекси и менува дозволи може да биде тешка за преглед и речиси невозможно чисто да се опорави од неа. Раздвојте ги промените на шемата од големите преместувања на податоци секогаш кога е можно.
Осмислете ги ограничувањата намерно
Ограничувањата се вредни бидејќи ги штитат податоците независно од кодот на апликацијата. Странски клуч, правило за уникатност или барање за не-null може да спречи класи на грешки што само валидацијата во апликацијата не може сигурно да ги запре.
Но, и ограничувањата имаат потреба од оперативно планирање. Додавањето ограничување за уникатност на постојни податоци ќе не успее ако веќе постојат дупликати. Пред да ја направите шемата построга, идентификувајте ги невалидните записи, одлучете како треба да се разрешат и осигурете се дека новите запишувања не можат да создадат дополнителни невалидни податоци за време на чистењето.
ALTER TABLE orders
ADD COLUMN external_reference VARCHAR(128);
CREATE UNIQUE INDEX orders_external_reference_unique
ON orders (external_reference);
Овој пример намерно е нецелосен како продукциско воведување. Пред да го создаде уникатниот индекс, тимот мора да одлучи дали NULL е дозволен, дали на сите постојни нарачки им се потребни вредности и како треба да се постапува со дупликатите. SQL синтаксата е лесниот дел; договорот за податоците е вистинската работа.
Раздвојте ја работата на шемата од пополнувањата на податоци наназад
Големите пополнувања наназад се чест извор на проблеми со миграциите. Една трансакција што ажурира голема табела може да потроши значајни ресурси, да генерира обемна активност на запишување и да го попречи нормалниот сообраќај. Исто така, може да ги надмине временските ограничувања за распоредување.
Наместо тоа, прво додајте ја новата колона, а потоа извршете процес за пополнување наназад во серии што може да се продолжи. Процесот треба да биде идемпотентен: повторното извршување на една серија не треба да ги оштети веќе обработените редови. Исто така, треба да пријавува напредок и да толерира прекин, бидејќи распоредувањата и работниците може да откажат.
Практична стратегија за серии е да се избере ограничен сет записи што сè уште имаат потреба од обработка, да се ажурираат и да се повтори. Избегнувајте потпирање на нестабилен offset низ множество податоци што се менува. Издржлив курсор со примарен клуч или експлицитен механизам за состојба на миграцијата обично е полесен за продолжување и разбирање.
Откако ќе заврши пополнувањето наназад, наметнете го конечниот услов само кога апликацијата доследно запишува валидни вредности. Ова секвенционирање спречува ново ограничување да ги прекине постарите инстанци на апликацијата за време на постепено распоредување.
Разберете ги заклучувањата, трансакциите и прозорците за распоредување
Не се однесуваат сите операции на базата на податоци исто. Некои промени на шемата се брзи ажурирања на метаподатоци; други може да скенираат табели, да обновуваат индекси или да добијат заклучувања што блокираат читања или запишувања. Поддршката за трансакциски DDL исто така варира според системот за бази на податоци и операцијата. Процесот на миграција не смее да претпостави дека секоја неуспешна промена на шемата автоматски ќе се поништи.
Пред да распоредите потенцијално скапа промена, тестирајте ја врз реалистична копија или репрезентативен обем на податоци. Измерете го изминатото време, проверете го однесувањето на заклучувањата и потврдете дали избраната база на податоци поддржува метод со помало влијание за таа операција. Ако операцијата не може безбедно да се вклопи во нормалните услови за распоредување, закажете ја како експлицитна оперативна промена наместо да ја криете во рутинско издание.
Во контејнеризирани средини, извршувајте ги миграциите како контролиран чекор при распоредување, наместо да дозволите секој контејнер на апликацијата да се натпреварува при стартување. Истовремените обиди за миграција може да создадат избегливи неуспеси, особено кога повеќе реплики почнуваат одеднаш. Една назначена задача или процес на издание треба да ги применува миграциите; инстанците на апликацијата треба да стартуваат само откако ќе биде достапна потребната верзија на шемата.
Враќањето назад не е секогаш правилниот план за опоравување
Тимовите често прашуваат дали секоја миграција има down миграција. Искрениот одговор е дека некои промени не се безбедно повратни. Откако податоците се трансформирани, избришани или запишани во нов формат, враќањето на шемата можеби нема да ја врати претходната состојба.
Посилната стратегија е поправка нанапред: прво направете адитивни промени, задржете ги старите структури за време на периодот на компатибилност и подгответе корективна миграција ако е потребно. Резервните копии остануваат суштински, но тие се механизам за опоравување од катастрофи, а не замена за внимателен дизајн на миграции.
За деструктивни операции, бидете експлицитни. Потврдете дека податоците се архивирани или намерно се отфрливи, проверете дека ниту една поддржана верзија на апликацијата не ја чита целната структура и осигурете се дека зависните задачи и интеграции продолжиле понатаму.
Вградете ги миграциите во инженерскиот работен тек
Сигурната практика за миграции е главно сигурна практика за преглед. Третирајте ги датотеките за миграција со истата сериозност како кодот за автентикација или промените на јавниот API. Рецензентите треба да поставуваат практични прашања:
- Може ли старите и новите верзии на апликацијата безбедно да работат за време на распоредувањето?
- Дали промената заклучува голема или многу користена табела?
- Дали е потребно пополнување на податоци наназад и може ли да продолжи по неуспех?
- Дали ограничувањата се валидни за постојните записи?
- Што се случува ако распоредувањето запре по оваа миграција?
- Дали промената може да се набљудува преку логови, метрики или проверка на базата на податоци?
Извршувајте миграции во континуирана интеграција врз празна база на податоци и, каде што е практично, врз база на податоци надградена од претходна верзија на шемата. Првото го потврдува почетното поставување. Второто ја потврдува патеката што вистинските продукциски системи навистина ја следат.
Еволуцијата на шемата е производ на трпение
Скалабилната backend архитектура не се состои само во избор на брза база на податоци или додавање повеќе инстанци на апликацијата. Таа значи менување на системот без секоја промена да биде коцкање. Добрите миграции се инкрементални, компатибилни, набљудливи и намерно здодевни.
Кога промените на шемата се дизајнирани како низа наместо како единствен настан, распоредувањата стануваат помирни, а опоравувањето станува поуправливо. Тоа е вистинската вредност на зрелата практика за миграции: ѝ овозможува на базата на податоци да се развива со исто темпо како производот, без да стане делот што сите се плашат да го допрат.