Migrácia databázy za prevádzky: kompatibilita, zámky a návrat
Postupné rozšírenie schémy v PostgreSQL, doplnenie dát po dávkach a kontrola zámkov. Čo musí platiť skôr, ako odstránite starý stĺpec.

Rýchle DDL môže rozbiť bežiacu aplikáciu
Nasadenie premenuje customers.legacy_code na external_code a spustí novú verziu aplikácie. Starý worker však ešte dokončuje export a stále číta pôvodný názov. SQL zlyhá, aj keď samotné premenovanie trvalo krátko. Pri postupnom nasadení navyše chvíľu obsluhujú požiadavky staré aj nové procesy. Meranie času migrácie preto samo osebe nepreukazuje kompatibilitu.
Nasledujúci príklad rieši ilustračnú zmenu názvu jedného textového poľa v PostgreSQL. Predpokladá, že legacy_code je vyplnený a jeho hodnotu možno preniesť bez zmeny významu. Nejde o univerzálny návod pre každú schému. Zmena typu, partitioning či presun do novej databázy potrebujú ďalšie rozhodnutia a testy. Prevádzka bez výpadku je cieľ overovania, nie vlastnosť garantovaná názvom migračného vzoru.
Rozšírenie a odstránenie rozdeľte do vydaní
Pri postupe expand–contract najprv pridáte nový prvok, kým starý zostáva dostupný. Aplikáciu upravíte tak, aby vedela pracovať s rozšírenou schémou. Až po prechode všetkých čitateľov a zapisovačov odstránite starý prvok. Do inventára patria webové procesy, workery, exporty, reporty, integrácie aj úlohy spúšťané zriedkavo.
V tomto príklade prvá kompatibilná verzia naďalej číta legacy_code, ale oba stĺpce zapisuje atomicky v rovnakej transakcii. Počas jej zavádzania ešte nemusí byť external_code správny všade. Backfill a nové čítanie preto spustite až po vyradení všetkých pôvodných zapisovačov. Inak by starý proces po doplnení dát zmenil iba legacy_code a vytvoril rozdiel, ktorý jednorazová kontrola nezachytí.
| Fáza | Čítanie a zápis | Podmienka ďalšieho kroku |
|---|---|---|
| Rozšírenie | Pridať nullable external_code; pôvodný kód beží ďalej. | DDL overené a schéma kompatibilná s pôvodnou aplikáciou. |
| Kompatibilná verzia | Čítať legacy_code; zapisovať oba stĺpce atomicky. | Všetci zapisovači vrátane workerov používajú nový zápis. |
| Doplnenie dát | Doplniť chýbajúce hodnoty po dávkach. | Žiadne chýbajúce hodnoty ani rozdiely podľa dohodnutého porovnania. |
| Nové čítanie | Čítať external_code; zatiaľ zapisovať oba stĺpce. | Všetci čitatelia prešli a návrat do kompatibilnej verzie bol overený. |
| Odstránenie | Prejsť na nový zápis; neskôr odstrániť legacy_code. | Skončilo okno návratu a starý stĺpec už nikto nepoužíva. |
Pred DDL zistite, na ktorý zámok čaká
PostgreSQL pri mnohých zmenách ALTER TABLE používa ACCESS EXCLUSIVE, ktorý koliduje aj s bežným čítaním. Krátky príkaz môže čakať za dlhou transakciou. Čakajúce DDL môže následne predĺžiť čakanie ďalších požiadaviek. Pred nasadením preto skúmajte aktívne transakcie a zámky na konkrétnej tabuľke, nielen plánovaný SQL súbor.
Pre samotnú migračnú reláciu môžete nastaviť lock_timeout a statement_timeout. Prvý obmedzí čakanie na zámok, druhý trvanie príkazu. Hodnoty musia vychádzať z prevádzkového rozpočtu; ukážkové dve sekundy sú ilustračné. Pri prekročení limitu sa tento krok preruší a ďalší pokus sa plánuje samostatne. Po zlyhaní príkazu v transakcii treba vykonať rollback, nie pokračovať nasledujúcim krokom.
Aj pridanie nullable stĺpca bez defaultu potrebuje zámok, hoci nevyžaduje hromadné doplnenie hodnôt. Pri iných zmenách môže PostgreSQL prepisovať tabuľku alebo skenovať existujúce dáta. Presné správanie overte podľa verzie databázy a konkrétneho príkazu. ORM pomenovanie „add field“ tieto prevádzkové rozdiely nevyjadruje.
BEGIN;
SET LOCAL lock_timeout = '2s';
SET LOCAL statement_timeout = '15s';
ALTER TABLE customers ADD COLUMN external_code text;
COMMIT;Backfill má byť obnoviteľná práca
Jeden UPDATE nad celou veľkou tabuľkou vytvorí dlhú transakciu a významnú záťaž. V ilustračnom postupe spracujte obmedzený počet riadkov, potvrďte dávku a až potom pokračujte. Veľkosť dávky nastavte meraním na reprezentatívnych dátach. Sledujte čas transakcií, zápisový objem, voľné miesto a prípadné oneskorenie replík. Päťsto riadkov v ukážke je počiatočný testovací parameter, nie odporúčanie pre každú databázu.
Dávka vyberá iba chýbajúce hodnoty a zamkne vybrané riadky. Zapisovači z kompatibilnej verzie musia oba stĺpce meniť v jednej transakcii. Pri súbehu tak databáza serializuje zápis do konkrétneho riadka; podmienka external_code IS NULL navyše zabráni prepísaniu už doplnenej hodnoty. Ak transformácia mení význam dát, toto jednoduché kopírovanie treba nahradiť osobitne overeným pravidlom.
Úloha musí po páde pokračovať bez opakovaného poškodenia údajov. Ukladajte postup a bezpečne opakujte nedokončenú dávku. Pri používaní SKIP LOCKED môže priechod vynechať práve obsadené riadky, preto vykonajte ďalšie priechody a záverečnú kontrolu. Postup kurzora ani nulový výsledok jednej dávky samy osebe nepotvrdzujú dokončenie.
WITH batch AS (
SELECT id
FROM customers
WHERE external_code IS NULL
ORDER BY id
LIMIT 500
FOR UPDATE SKIP LOCKED
)
UPDATE customers AS customer
SET external_code = customer.legacy_code
FROM batch
WHERE customer.id = batch.id
AND customer.external_code IS NULL;Index a obmedzenie plánujte samostatne
Nový spôsob čítania môže potrebovať index. CREATE INDEX CONCURRENTLY obmedzí blokovanie bežných zápisov, má však odlišné pravidlá od obyčajného CREATE INDEX. Nemožno ho spustiť v transakčnom bloku a pri zlyhaní môže zostať neplatný index. Prevádzkový postup preto musí overiť výsledný stav a riešiť upratanie pred ďalším pokusom; samotná existencia názvu indexu nestačí.
Obmedzenia pridávajte až po pochopení existujúcich dát a nových zápisov. Pre vhodný CHECK alebo cudzí kľúč možno oddeliť pridanie NOT VALID od neskoršieho VALIDATE CONSTRAINT. Nové zápisy už musia pravidlo rešpektovať, aj keď staré riadky ešte nie sú overené. Tento mechanizmus nie je univerzálnou skratkou pre každý typ obmedzenia, napríklad UNIQUE má iný postup.
V ukážkovej zmene najprv overte vyplnenosť a zhodu hodnôt. Ak má byť nové pole povinné alebo unikátne, jeho pravidlo patrí do samostatného migračného kroku s vlastným testom. Nezlučujte kopírovanie dát, vytvorenie indexu a odstránenie starého stĺpca do jedného kroku len preto, aby bol deployment kratší na papieri.
Návrat k aplikácii a návrat k dátam sú odlišné
Po prepnutí čítania môžete v tomto modeli vrátiť kompatibilnú verziu, ktorá stále číta legacy_code a zapisuje oba stĺpce. Pôvodná verzia, ktorá zapisuje iba starý stĺpec, má iné dôsledky: pred opätovným zapnutím nového čítania by bolo potrebné obnoviť synchronizáciu. Povolený cieľ rollbacku preto označte konkrétnou verziou, nie všeobecným „vrátime predchádzajúci release“.
Po zastavení dvojitého zápisu sa staré dáta môžu prestať aktualizovať. Odstránenie stĺpca navyše ruší jednoduchú cestu návratu. Záloha je potrebná, ale jej obnova môže vrátiť aj iné dáta do staršieho času a vyžaduje zosúladenie nových zápisov. Pred deštruktívnym krokom preto overte obnovu, dohodnite zodpovednosť a ukončite okno podporovaného návratu.
Zhodu merajte pred prepnutím aj počas neho
Kontrola počtu riadkov neodhalí nesprávnu hodnotu na správnom mieste. V tomto príklade sledujte chýbajúce external_code aj rozdiely medzi starým a novým stĺpcom, pri ktorých má byť obsah totožný. Vyhodnocujte ich na konzistentnom databázovom pohľade. Pri transformáciách pripravte aj kontrolu významu, referencií a reprezentatívnych výstupov aplikácie.
Súčasťou prepnutia má byť pozorovanie chybovosti a času odpovedí. PostgreSQL poskytuje údaje o aktivite a čakaní; spojte ich s metrikami aplikácie, aby sa dalo rozlíšiť čakanie na zámok od pomalého plánu dotazu. Doplnenie dát samo osebe nepreukazuje, že nové čítanie má vhodný index alebo že starý report používa správnu schému.
Pred nasadením nacvičte aj zastavenie
Reprezentatívny test potrebuje realistický objem a rozdelenie dát, súbežné zápisy a dlhú transakciu držanú počas DDL. Malá prázdna databáza overí syntax, ale nepovie veľa o čakaní ani o zápisovej záťaži. Počas skúšky zastavte backfill, obnovte ho a vráťte kompatibilnú verziu aplikácie.
Pre každý krok určte podmienku pokračovania a prerušenia. Ak rastie čakanie alebo sa objavia rozdielne hodnoty, proces má zastaviť ďalší prechod a zachovať kompatibilnú schému. Pri malej aplikácii môže byť krátke dohodnuté servisné okno lacnejšie a prehľadnejšie než niekoľko vydaní s dvojitým zápisom. Výber treba oprieť o prevádzkové požiadavky a výsledky skúšky.
- Pôvodná aplikácia funguje po rozšírení schémy a všetci zapisovači sú započítaní.
- DDL skončí podľa limitu pri blokujúcej transakcii a následný pokus je riadený.
- Backfill po páde pokračuje a pri súbežnej úprave neprepíše novšiu hodnotu.
- Záverečná kontrola nájde aj riadky vynechané cez SKIP LOCKED.
- Nové čítanie prejde s dohodnutým výkonom aj po návrate do kompatibilnej verzie.
- Starý stĺpec sa odstráni až po overení všetkých čitateľov a ukončení okna návratu.
Zdroje a dokumentácia
Pri implementácii skontrolujte dokumentáciu verzie, ktorú používate.
Od témy ku konkrétnemu riešeniu.
Súvisiaca realizácia: FaxCopy a.s.
