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.

Kategorie: DatabázeAktualizováno

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

  1. Čí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;
  2. 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

  1. PostgreSQL Documentation: Materialized Views(otevře se v novém okně)PostgreSQL Global Development Group
  2. Data Modeling Introduction(otevře se v novém okně)MongoDB
  3. Denormalization(otevře se v novém okně)Wikipedia
  4. Best practices for DynamoDB design(otevře se v novém okně)Amazon Web Services

Související pojmy

Potřebujete to vyřešit v praxi?

Poradíme, jak na to ve vašem projektu

Vysvětlit pojem je jedna věc, navrhnout kolem něj funkční řešení druhá. Ozvěte se a probereme, co dává smysl u vás.