TakéDenormalizace dat, Denormalizace databázePokročilý
Definice
Denormalizace je záměrné zavedení redundance do datového modelu, typicky zkopírováním nebo předpočítáním hodnot z jiných tabulek, aby čtení nemuselo spojovat mnoho tabulek a agregovat je za běhu. Denormalizace zrychluje dotazy výměnou za větší objem dat, složitější zápisy a riziko, že se kopie hodnoty rozejdou s originálem.
Co denormalizace mění v datovém modelu
Denormalizace obrací kompromis, který nastavuje normalizace databáze. Normalizovaný model ukládá každý fakt právě jednou, takže zápis je levný a nemůže vzniknout rozpor. Cenu za to platí čtení: aby dotaz složil užitečnou odpověď, musí projít několik tabulek přes JOIN a spočítat agregace. Denormalizace část těchto výpočtů přesune dopředu a uloží jejich výsledek do sloupce, který je logicky odvozený.
V praxi má denormalizace několik podob. Zkopírovaný atribut drží v tabulce objednávek název a cenu produktu v okamžiku prodeje. Předpočítaný čítač uchovává počet komentářů u článku. Sloučená tabulka spojí data, která model rozděloval do vztahu 1:1. Materializovaný pohled uloží výsledek dotazu jako fyzickou tabulku, kterou lze indexovat.
Kdy se čtení vyplatí víc než čistota
Denormalizace dává smysl tam, kde poměr čtení k zápisům je výrazně nakloněn čtení a kde konkrétní dotaz je měřitelně pomalý. Typicky jde o výpisy na landing pages, reporting nad velkými fakty, veřejné katalogy nebo API, které musí odpovědět v jednotkách milisekund. Analytické sklady jdou touto cestou schválně: hvězdicové schéma je denormalizované záměrně, protože se do něj nahrává dávkově a čte se z něj neustále.
Denormalizace naopak nepomůže, když je dotaz pomalý kvůli chybějícímu indexu, špatně napsané podmínce nebo N+1 dotazům z ORM. Redundance v takovém případě jen skryje původní příčinu a přidá k ní novou třídu chyb.
Kdo hlídá, aby se kopie nerozešla s originálem
Klíčová otázka denormalizace nezní, jestli redundanci zavést, ale kdo bude udržovat konzistenci. Možnosti jsou v podstatě čtyři: databázový trigger, aplikační vrstva při každém zápisu, periodický přepočet dávkou, nebo materializovaný pohled obnovovaný příkazem. Každá varianta má jinou latenci a jiný způsob, jak selže. Trigger je spolehlivý, ale zdražuje zápis a snadno se přehlédne. Aplikační logika se rozpadne v okamžiku, kdy do databáze sáhne migrace nebo skript mimo ni.
Praktické minimum: každý odvozený sloupec musí mít v repozitáři popsaný zdroj pravdy a existovat skript, který dokáže rozdíl mezi kopií a originálem najít. Bez toho se nekonzistence objeví až v reklamaci od zákazníka.
Denormalizace v dokumentových databázích
V NoSQL systémech není denormalizace výjimkou, ale výchozím návrhem. Dokumentové databáze doporučují vnořovat data, která se čtou společně, protože atomicita je zaručena na úrovni jednoho dokumentu. Klíč-hodnota úložiště i široce sloupcové modely typu Cassandra jdou ještě dál a modelují se od dotazu: pro každý přístupový vzor vzniká vlastní tabulka s vlastní kopií dat. Zápis pak zapisuje na několik míst, což je vědomá součást návrhu.
Příklady z praxe
Čítač komentářů u článku
Diskuzní web počítal komentáře agregací nad tabulkou s desítkami milionů řádků při každém zobrazení výpisu. Přidání sloupce comment_count do tabulky článků a jeho údržba triggerem srazila dotaz na jediné čtení indexu. Cenou je nutnost přepočítacího skriptu, který po hromadném mazání spamu čísla srovná.
ALTER TABLE articles ADD COLUMN comment_count integer NOT NULL DEFAULT 0; CREATE FUNCTION bump_comment_count() RETURNS trigger AS $$ BEGIN UPDATE articles SET comment_count = comment_count + 1 WHERE id = NEW.article_id; RETURN NEW; END; $$ LANGUAGE plpgsql;Historická cena na položce objednávky
E-shop původně dohledával cenu položky přes vazbu na produkt. Po změně ceníku se ale přepsaly i staré faktury a účetní přestala souhlasit s výpisem z brány. Řešením byla denormalizace: položka objednávky si při vytvoření uloží vlastní kopii názvu, ceny a sazby DPH. Tady redundance není optimalizace, ale správný model, protože historická cena je jiný fakt než aktuální.
Časté omyly
- MýtusDenormalizace je jen výmluva pro nepořádek v databázi.
- Ve skutečnostiDenormalizace je vědomé rozhodnutí učiněné nad normalizovaným modelem, u kterého je pojmenovaný zdroj pravdy a způsob údržby kopie. Nepořádek vzniká tehdy, když redundance nikdy nebyla navržena, jen se nabalila.
- MýtusDenormalizace vždycky zrychlí aplikaci.
- Ve skutečnostiDenormalizace zrychlí čtení, ale zdraží zápis, zvětší data i indexy a může snížit počet řádků na stránce v cache. U systému s vysokým podílem zápisů bývá výsledek horší než původní stav.
- MýtusV NoSQL se nenormalizuje, takže tam denormalizace nemá smysl řešit.
- Ve skutečnostiV dokumentových a široce sloupcových databázích je denormalizace hlavní nástroj návrhu a je třeba ji řešit ještě pečlivěji: chybí cizí klíče i JOIN, takže veškerou konzistenci kopií drží aplikace.
Časté dotazy
- Jak poznám, že je čas denormalizovat?
- Denormalizace přichází na řadu až po měření. Nejprve je potřeba mít plán dotazu (EXPLAIN ANALYZE nebo ekvivalent), z něhož je vidět, že čas spotřebují opakované spojení a agregace nad velkým objemem dat, a ne chybějící index či nevhodná podmínka. Dalším signálem je poměr čtení k zápisům: pokud se sloupec čte tisíckrát na jeden zápis, předpočítání se vyplatí. Pokud se dotaz volá jednou denně v reportu, denormalizace přidá riziko bez užitku.
- Je materializovaný pohled totéž co denormalizace?
- Materializovaný pohled je jedna z forem denormalizace, u které údržbu redundance přebírá databáze místo aplikace. Schéma zůstane normalizované, odvozená data žijí vedle něj jako fyzicky uložený výsledek dotazu. V PostgreSQL se obnovuje příkazem REFRESH MATERIALIZED VIEW, případně souběžně s variantou CONCURRENTLY. Výhodou je jasně daný zdroj pravdy a snadné zahození, nevýhodou latence mezi obnovami: data jsou vždy o něco stará. Pro účely, kde stačí čerstvost v řádu minut, je to obvykle bezpečnější volba než ruční kopírování sloupců.
- Jak zajistit, aby se denormalizovaná data nerozešla?
- Konzistenci denormalizovaných dat drží kombinace tří věcí. Zaprvé jediné místo zápisu: buď trigger v databázi, nebo jedna funkce v aplikaci, kterou nikdo neobchází. Zadruhé transakce, která původní i odvozený zápis provede společně, aby nemohl projít jen jeden z nich. Zatřetí kontrolní úloha, která pravidelně porovná odvozenou hodnotu s přepočtem ze zdroje a rozdíl nahlásí nebo opraví. Bez třetího bodu se drift objeví, jen se o něm dozvíte pozdě.
- Ruší denormalizace normální formy?
- Denormalizace normální formy neruší, ale vědomě z nich ustupuje na konkrétních místech. Rozumný postup je navrhnout model ve třetí normální formě, ověřit ho na skutečných dotazech a teprve pak vybrané sloupce nebo tabulky denormalizovat. Zbytek schématu zůstane normalizovaný a slouží jako zdroj pravdy, ze kterého lze odvozená data kdykoli spočítat znovu. Denormalizace bez předchozího normalizovaného návrhu obvykle skončí modelem, u kterého už nikdo neví, která kopie hodnoty je ta správná.
Zdroje
- PostgreSQL Documentation: Materialized Views(otevře se v novém okně)
- Data Modeling Introduction(otevře se v novém okně)
- Denormalization(otevře se v novém okně)
- Best practices for DynamoDB design(otevře se v novém okně)