Veritabanlarında Denormalizasyon: Ne Zaman ve Nasıl Kullanılır
Denormalizasyonun, join’leri azaltıp sorguları basitleştirerek okuma performansını nasıl artırdığını öğrenin. Güçlü bir araç hâline getiren takasları, teknikleri ve kullanım alanlarını anlayın.
I’ve, normalleştirme ve bunun veri bütünlüğü için neden bu kadar güvenilir bir temel olduğuna dair çok şey yazdım. Kendimle çelişmiyorum. İyi normalleştirilmiş bir şemanın çoğu işlemsel sistem için doğru başlangıç noktası olduğuna hâlâ inanıyorum. Bu makale, okuma performansının katı normal forma göre daha önemli olduğu çok belirli durumlarda, bu saflığa bilinçli olarak karşı çıkma seçimini ele alıyor.
Denormalizasyon, normalleştirmeyi atlamak değildir. Gerçek sorgular, gerçek kullanıcılar ve gerçek SLA’ler size join’lerin ve anlık hesaplamaların çok yavaş veya çok maliyetli olduğunu söylediğinde, normalleştirilmiş bir modele uyguladığınız bir performans optimizasyonudur. Pratikte daha hızlı okumalar ve daha basit sorgular için daha fazla depolama, daha karmaşık yazmalar ve ek tutarlılık çalışmasıyla takas yaparsınız.
Bu makalede denormalizasyonun nerede faydalı olduğunu, nerede olmadığını ve SQL veritabanlarında nasıl güvenle uygulanacağını göstereceğim. Amaç, iyi tasarımı terk etmek değil; iş yükü haklı çıkardığında onun üzerine kestirme yolları katmanlamak.
Veritabanlarında Denormalizasyon Nedir?
Normalleştirme ve veritabanı tasarımına aşinaysanız, kısa cevap şöyle:
Denormalizasyon, daha hızlı okumalar ve daha basit sorgular için, önceden normalleştirilmiş bir şemaya kasıtlı olarak yinelenen veri ekleme eylemidir. Bu, iyi modellemeyi atlamak için bir bahane değil, hedefli bir performans optimizasyonudur!
Veritabanı tasarımında nispeten yeniyseniz, “normalleştirilmiş”, “denormalize” ve “normalleştirilmemiş” kavramlarını açmak faydalı olabilir. Bu üç terimi çevrimiçi ortamda sık duyarsınız ve birbirine karıştırmamak önemlidir.
Normalleştirilmiş: Veriler, yinelenmeyi en aza indiren ve bütünlüğü koruyan iyi yapılandırılmış tablolara bölünür (bkz. 3NF/BCNF).
Denormalize: Normalleştirilmiş modelin üzerine, yaygın okumaları hızlandırmak için ekstra sütunlar, önceden hesaplanmış değerler veya önceden birleştirilmiş tablolarla seçici yinelenmeyi yeniden eklersiniz. Denormalizasyonda tek bir gerçek kaynağını (normalleştirilmiş tablolar) korur, ardından gösterge tabloları, ürün listeleri, arama vb. gibi sıcak yollar için daha hızlı bir veya daha fazla temsili sürdürürsünüz. Depolama + yazma karmaşıklığını, okuma hızı + daha basit sorgularla takas edersiniz.
Normalleştirilmemiş: Yapısı ve kısıtları düzgün tasarlanmamış ham, ad hoc veya dağınık veri. Burada yaptığımız bu değil.
SQL’de örnek
Normalleştirilmiş ve denormalize modelleme arasındaki farkı canlandırmanıza yardımcı olacak küçük bir SQL örneği:
Normalleştirilmiş modelleme
-- Source of truthCREATETABLE customers (
customer_id BIGINTPRIMARYKEY,
name TEXTNOTNULL,
tier TEXTNOTNULL);CREATETABLE orders (
order_id BIGINTPRIMARYKEY,
customer_id BIGINTNOTNULLREFERENCES customers(customer_id),
order_total NUMERIC(12,2)NOTNULL,
created_at TIMESTAMPNOTNULLDEFAULTnow());-- Typical report needs a joinSELECT c.name, c.tier,SUM(o.order_total)AS revenue
FROM orders o
JOIN customers c ON c.customer_id = o.customer_id
GROUPBY c.name, c.tier;Altyapı:
Bir raporlama yolu için denormalize
-- Add redundant fields for faster readsALTERTABLE orders
ADDCOLUMN customer_name TEXT,ADDCOLUMN customer_tier TEXT;-- Now most reports avoid the joinSELECT customer_name, customer_tier,SUM(order_total)AS revenue
FROM orders
GROUPBY customer_name, customer_tier;Altyapı:
Bu örnekte, customers tablosunu gerçek kaynağı olarak tutar ve orders.customer_name / orders.customer_tieralanlarının senkron kalmasını sağlarsınız (örneğin, tetikleyici, CDC işi veya zamanlanmış bir backfill ile). Aynı gerçek, iki temsil; her biri farklı bir iş için optimize edilmiştir.
Denormalizasyon kötü tasarım mıdır?
Denormalizasyonun sadece kötü tasarımın bir ürünü olduğuna dair yaygın bir yanlış kanı var. Ölçülü, kapsamı belirli ve bakımı yapılırsa durum böyle değildir. Evet, denormalizasyon sıklıkla daha yüksek normal formları ihlal eder (zaten amacı budur), ancak bu kasıtlıdır ve tutarlılığı korumaya yönelik bir planla desteklenir. Kötü tasarım, normalleştirmeyi tamamen atlamak ya da bir senkronizasyon stratejisi olmadan gelişi güzel yinelenme serpiştirmektir.
Veritabanı tasarımı hakkında daha fazla bilgi edinmek isterseniz, konuyla ilgili başlangıç kursumuz iyi bir başlangıç noktasıdır!
Normalleştirme ve Denormalizasyon
İşte yan yana bir karşılaştırma. Kapsamlı değildir ancak her yaklaşımın takaslarını anlamanıza yardımcı olur.
Boyut
Normalleştirme
Denormalizasyon
Birincil amaç
Bütünlük, minimum yinelenme, kolay & doğru güncellemeler
Daha hızlı okumalar, sıcak yollarda daha basit sorgular
En uygun
Sık yazma/güncelleme yapılan OLTP sistemleri
Okuma ağırlıklı gösterge tabloları, arama/listeleme sayfaları, raporlama/analitik
Okuma performansı
Sıklıkla join gerektirir, doğru indekslerle iyidir
Daha az join, çok daha hızlı ve öngörülebilir olabilir
Yazma karmaşıklığı
Basit: tek gerçek kaynağı
Daha yüksek: yinelenen kopyalar veya özetlerin güncellenmesi/senkronu gerekir
Depolama
Sade
Daha büyük (ek sütunlar/tablolar, önceden hesaplanmış görünümler)
Veri bütünlüğü
Tasarım gereği güçlü (3NF/BCNF kısıtları)
Sapmayı önlemek için mekanizmalar gerektirir (tetikleyiciler, CDC, işler)
Değişim hızı
Sütun yeniden adlandırma/güncellemeleri yerelleştirilmiştir
Değişiklikler çoğaltılmış veriler boyunca yayılabilir
Operasyonel ek yük
Daha düşük: daha az hareketli parça
Daha yüksek: yenileme politikaları, backfill’ler, izleme
Arıza biçimleri
N+1 sorgular, yavaş join’ler, eksik indeksler
Bayat veri, tutarsızlık, yazma amplifikasyonu
Şema evrimi
Öngörülebilir, refactor-dostu
Yinelenmiş temsiller için geçiş planları gerekir
Tipik örnekler
Siparişler, müşteriler, işlemler
Önceden join’lenmiş ürün listeleri; toplu satış tabloları; materialized/indeksli görünümler
Neden ve Ne Zaman Denormalize Edilir
Gerçek kullanıcılar ve gerçek sorgular, join’ler, toplulaştırmalar veya tekrarlanan aramalar nedeniyle tıkandığında denormalizasyon gerçekten faydalı hâle gelir. İndeksleme, sorgu ayarı ve önbelleklemenin yeterli olmadığını doğruladıktan sonra, öngörülebilir erişim desenlerinde hızlı okumalar için denormalizasyona yönelebilirsiniz.
Denormalizasyonun yardımcı olabileceği durumlar
Okuma ağırlıklı iş yükleri:p95/p99 gecikmeleri join’ler veya toplulaştırmalar tarafından domine ediliyor, CPU hash/merge join’lerde harcanıyor, buffer cache devri yüksek.
Stabil sorgu şekilleri:Aynı gösterge tabloları/uç noktalar gün boyu benzer filtrelerle çalışıyor (ör. dünkü satışlar kategori bazında).
Fan-out join’ler:Tek bir sıcak tablo, bir kart ya da liste görünümü oluşturmak için 3–5 başka tabloyla join oluyor.
Toplulaştırma sıcak noktaları:Sürekli olarak geniş aralıklarda toplamlar, sayımlar veya en son değerleri hesaplıyorsunuz.
SLA baskısı:Mevcut planların diske döktüğü veya çok tarama yaptığı yerde ürün, 200 ms altı yanıtlar istiyor.
Klasik kullanım alanları
Gösterge tabloları & BI raporlama (OLAP/analitik):Günlük/aylık toplulaştırmaları önceden hesaplayın, pahalı group-by’ların materialized görünümlerini tutun veya yaygın dilimler için denormalize edilmiş olgu tablolarını saklayın.
E-ticaret/katalog & arama/listeleme sayfaları:category_name, brand_name, price_with_tax gibi alanları veya hızlı listeler ve filtreler için önceden join’lenmiş bir ürün projeksiyonunu çoğaltın.
CMS/blog/haber akışları:Makale/gönderi tablosunda author_name, primary_topic veya rendered_excerpt alanlarını saklayarak join’leri veya çalışma zamanı dönüşümlerini önleyin.
Aktivite akışları & sayaçlar:like_count, follower_count, veya latest_comment_at gibi türetilmiş öznitelikleri, yeniden hesaplamak yerine saklayın.
Büyük ölçekte olay/kayıt analitiği (OLAP/NoSQL):Sütunlu depolar için iç içe verileri düzleştirin ve taramaları öngörülebilir kılmak için bölüm dostu geniş satırlar tutun.
Ne zaman denormalize etmemeli ve önce başka bir şey kullanmalı
Sıkı tutarlılık gerektiren yazma ağırlıklı OLTP (siparişler, ödemeler, stok ayarlamaları).
Darboğaz eksik/zayıf indeksler, N+1 sorgular veya geveze ORM ise. Önce bunları düzeltin.
Yüksek oynaklıktaki alanlar (ör. her saniye değişen ürün bulunabilirliği) — çoğaltma devri artırır.
Açık bir sahiplik ve senkron planı olmayan ekipler (tetikleyiciler, CDC/işler, yenileme politikaları, sapma izleme). Bu yoksa, iyiden çok zarar verme riski alırsınız!
Veri kümesi o kadar küçük ki bir kaplayan indeks veya önbellek hâlihazırda hızlı yapıyor.
Denormalizasyonu, size en az ek operasyonel yükle en fazla okuma hızını sağladığı yerlerde ve yalnızca daha ucuz çözümleri elediğinizde kullanmak istersiniz.
Denormalizasyonun şu an için tam olarak ihtiyacınız olan şey olmadığını fark ettiyseniz, bekleyin! Bir sonraki bölümde denormalizasyona alternatifleri keşfedeceğiz.
Denormalizasyona Alternatifler
Denormalizasyon her sorununuzun cevabı değildir. Yinelenme eklemeden önce, motor ve uygulamanızdan alabileceğiniz her şeyi zorlamalısınız. Bu düzeltmelerin bakımı daha ucuzdur ve sıklıkla aynı kazanımları sağlar.
1) İndeksleme
Bileşik/kaplayan indeksler: SQL sorgularınızda, filtre sütunlarını önce, ardından GROUP BY/ORDER BY sütunlarını koyun. Motorun sorguyu yalnızca indeksten sunabilmesi için seçili liste sütunlarını dahil ediyorum.
Filtreli/kısmi indeksler: Yalnızca sıcak dilimi indeksleyin (ör. status = 'ACTIVE'); indeksi küçük ve hızlı tutar.
İfade/fonksiyonel indeksler:LOWER(email) veya date_trunc('day', created_at) üzerinde indeks oluşturup hesaplanmış taramalardan kaçının.
2) Sorgu ayarı & sayfalama
SELECT * kullanmaktan kaçının. Yalnızca görüntülediğinizi alın.
Yalnızca varlık kontrolü gerektiğinde gereksiz join’leri EXISTS/SEMI ile değiştirin.
Koşulları aşağı itin: erken filtreleyin, geç toplulaştırın.
Stabil, hızlı kaydırma için anahtar kümesi sayfalama (WHERE created_at < ? ORDER BY created_at DESC LIMIT 50) kullanın.
3) Önbellekleme
Uygulama önbelleği (ör. Redis) kullanarak sıcak sorguları ve işlenmiş parçaları önbelleğe alın.
HTTP önbellekleme (ETag/Last-Modified) ile herkese açık sayfalar ve gösterge tabloları için faydalanın.
Kısa ömürlü önbellekler (30–120 sn) sıklıkla şema değişikliği ihtiyacını ortadan kaldırır.
4) Okuma replikaları
Ağır okumaları replikalara aktarın. Gösterge tabloları ve dışa aktarmalar için harikadır.
5) Bölümlendirme & budama
Büyük tabloları aralık/karma bölümlendirin ki taramalar yalnızca ilgili bölümlere dokunsun (ör. son 30 gün). Bu arada bu denormalizasyon değildir; yalnızca taranan veri miktarını azaltmaktır.
6) Sütunlu/OLAP depoları
Ağır analitiği Snowflake/BigQuery/ClickHouse’a (ELT/dbt ile) taşıyın. OLTP’yi normalleştirilmiş tutun ve depo, geniş ve tarama dostu şekilleri yönetsin.
7) ORM hijyeni
N+1 sorguları öldürün (hemen yükleme veya toplu), makul seçim listeleri belirleyin ve sayfa boyutlarını sınırlayın. Temiz bir ORM katmanı denormalizasyon ihtiyacını ortadan kaldırabilir.
8) Hesaplanan/üretilen sütunlar (VT tarafından yönetilen)
Türetilmiş değerleri (ör. price_with_tax) üretilen/hesaplanan sütunlar veya ifade indeksleri olarak veritabanına yönettirin. Böylece uygulama seviyesinde senkron mantığı yazmadan hızlı okumalar elde edersiniz.
Denormalizasyon Teknikleri
Normalleştirilmiş bir modele kontrollü yinelenme eklemenin en yaygın yolları burada. Her biri için ne yaptığını, ne zaman kullanılacağını ve nasıl senkron tutulacağını göstereceğim.
Kurulum: çekirdekte customers, orders, order_items, products bulunan normalleştirilmiş bir yapı varsayın.
1) Düzleştirilmiş / önceden join’lenmiş “projeksiyon” tabloları
Ne: Hızlı okumalar için ihtiyaç duyduğunuz sütunları önceden join’leyen bir tablo oluşturun (ör. ürün listesi veya siparişler panosu).
Ne zaman: Sıcak yolunuz 3 ila 5 tabloyu öngörülebilir biçimlerde join’liyorsa.
Nasıl (PostgreSQL):
-- Read-optimised projection for a typical orders listCREATETABLE orders_projection (
order_id BIGINTPRIMARYKEY,
created_day DATENOTNULL,
customer_id BIGINTNOTNULL,
customer_name TEXTNOTNULL,
total_amount NUMERIC(12,2)NOTNULL);-- Initial backfillINSERTINTO orders_projection (order_id, created_day, customer_id, customer_name, total_amount)SELECT o.order_id,
o.created_at::dateAS created_day,
c.customer_id,
c.name AS customer_name,SUM(oi.quantity * oi.unit_price)AS total_amount
FROM orders o
JOIN customers c ON c.customer_id = o.customer_id
JOIN order_items oi ON oi.order_id = o.order_id
GROUPBY o.order_id, created_day, c.customer_id, c.name;-- Index for fast filtering by day/customerCREATEINDEXON orders_projection (created_day, customer_id);Altyapı:
Senkron seçenekleri:
Yazma sonrası iş ("order.updated" kuyruğa al → satırı yeniden hesapla)
Gecelik/15 dakikalık toplu iş (dbt/cron)
2) Yinelenen sütunlar (birkaç özniteliği kopyala)
Ne: Sık okunan birkaç özniteliği, join’lerden kaçınmak için başka bir tabloya çoğaltın (ör. orders.customer_name).
Ne zaman: Yalnızca 1 veya 2 değere ihtiyacınız varsa ve tam bir projeksiyon istemiyorsanız.
Nasıl (PostgreSQL tetikleyici örneği):
ALTERTABLE orders
ADDCOLUMN customer_name TEXT,ADDCOLUMN customer_tier TEXT;-- Keep the redundant fields correct on insert/updateCREATEORREPLACEFUNCTION sync_order_customer_fields()RETURNSTRIGGERAS $
BEGINSELECT name, tier INTO NEW.customer_name, NEW.customer_tier
FROM customers WHERE customer_id = NEW.customer_id;RETURN NEW;END;
$ LANGUAGE plpgsql;CREATETRIGGER trg_orders_sync_customer
BEFORE INSERTORUPDATEOF customer_id ON orders
FOR EACH ROWEXECUTEFUNCTION sync_order_customer_fields();-- Propagate customer name/tier changes to existing ordersCREATEORREPLACEFUNCTION propagate_customer_changes()RETURNSTRIGGERAS $
BEGINUPDATE orders
SET customer_name = NEW.name,
customer_tier = NEW.tier
WHERE customer_id = NEW.customer_id;RETURN NEW;END;
$ LANGUAGE plpgsql;CREATETRIGGER trg_customer_propagate
AFTERUPDATEOF name, tier ON customers
FOR EACH ROWEXECUTEFUNCTION propagate_customer_changes();Altyapı:
Takas: Okumalar basittir, ancak bir müşteri güncellendiğinde yazmalar yayılarak artar.
Ne zaman: Hesaplama sürekli yapılıyor ve determinizm var.
İki yaygın kalıp:
Üretilen sütun (VT yazmada yeniden hesaplar: kontrolünüz daha az, tablo dışı referans yok)
Saklanan sütun + tetikleyici/iş (ne zaman/nasıl değişeceğini siz kontrol edersiniz)
-- Generated column example (Postgres 12+; same-table expressions)ALTERTABLE products
ADDCOLUMN price_with_tax NUMERIC(12,2) GENERATED ALWAYS AS(price *1.20) STORED;-- Counter maintained by triggers (likes per post)ALTERTABLE posts ADDCOLUMN like_count INTEGERNOTNULLDEFAULT0;CREATEORREPLACEFUNCTION inc_like_count()RETURNSTRIGGERAS $
BEGINUPDATE posts SET like_count = like_count +1WHERE post_id = NEW.post_id;RETURN NEW;END; $ LANGUAGE plpgsql;CREATETRIGGER trg_like_insert
AFTERINSERTON post_likes
FOR EACH ROWEXECUTEFUNCTION inc_like_count();CREATEORREPLACEFUNCTION dec_like_count()RETURNSTRIGGERAS $
BEGINUPDATE posts SET like_count = GREATEST(like_count -1,0)WHERE post_id = OLD.post_id;RETURN OLD;END; $ LANGUAGE plpgsql;CREATETRIGGER trg_like_delete
AFTERDELETEON post_likes
FOR EACH ROWEXECUTEFUNCTION dec_like_count();Altyapı:
4) Toplam/özet tabloları
Ne: Günlük gelir, günlük aktif kullanıcılar, kategori başına siparişler gibi rollup’ları önceden hesaplayın.
Ne zaman: Gösterge tabloları aynı group-by’ları tekrar ediyor ve ham taramalar pahalı.
CREATETABLE daily_sales (
sales_day DATEPRIMARYKEY,
gross_amount NUMERIC(14,2)NOTNULL,
order_count INTEGERNOTNULL);-- Incremental upsert for "yesterday" (run hourly/15-min)INSERTINTO daily_sales (sales_day, gross_amount, order_count)SELECT(o.created_at AT TIME ZONE 'UTC')::dateAS sales_day,SUM(oi.quantity * oi.unit_price)AS gross_amount,COUNT(DISTINCT o.order_id)AS order_count
FROM orders o
JOIN order_items oi ON oi.order_id = o.order_id
WHERE o.created_at >= date_trunc('day',now()-interval'1 day')AND o.created_at < date_trunc('day',now())GROUPBY sales_day
ON CONFLICT (sales_day)DOUPDATESET gross_amount = EXCLUDED.gross_amount,
order_count = EXCLUDED.order_count;Altyapı:
Senkron seçenekleri: zamanlanmış iş (cron/dbt), akış güncellemeleri (CDC) veya olay güdümlü toplayıcılar.
5) Materialized view’lar (kalıcı sorgu sonuçları)
Ne: Pahalı bir sorgunun çıktısını indeksleyebileceğiniz fiziksel bir tablo olarak saklayın.
Ne zaman: Sonuç kümesi pahalı, nispeten stabil ve yenileme pencerelerini tolere edebiliyorsanız.
-- Expensive reportCREATE MATERIALIZED VIEW mv_category_sales ASSELECT p.category_id,
date_trunc('day', o.created_at)ASday,SUM(oi.quantity * oi.unit_price)AS revenue
FROM orders o
JOIN order_items oi ON oi.order_id = o.order_id
JOIN products p ON p.product_id = oi.product_id
GROUPBY p.category_id, date_trunc('day', o.created_at);-- Make it fast to queryCREATEINDEXON mv_category_sales (category_id,day);-- Refresh patterns-- Full, blocking:
REFRESH MATERIALIZED VIEW mv_category_sales;-- Non-blocking reads (requires unique index on the MV):-- 1) ensure a unique index exists (e.g., (category_id, day))-- 2) then:
REFRESH MATERIALIZED VIEW CONCURRENTLY mv_category_sales;Altyapı:
Takas: Postgres’te artımlı yenileme yerel değildir; bu nedenle yenileme temposunu yönetirsiniz. Yine de, dbt veya özel “yalnızca ekle + pencereli yeniden kur” kalıpları kullandığınızda yeterince kolaydır.
6) Önceden hesaplanmış JSON “belge” projeksiyonları
Ne: API/UI ihtiyaçlarınıza uyan denormalize bir JSON yığını (ör. ürün kartı) saklayın ve yanında hedefli JSON indeksleri bulundurun.
Ne zaman: API’niz aynı şekli tekrarlı sunuyorsa ve tek okumayla iş yapmak istiyorsanız.
CREATETABLE product_cards (
product_id BIGINTPRIMARYKEY,
card_json JSONB NOTNULL);-- Build or rebuild card documentsINSERTINTO product_cards (product_id, card_json)SELECT p.product_id,
jsonb_build_object('id', p.product_id,'name', p.name,'brand', b.name,'price_with_tax', p.price *1.20,'category', c.name
)FROM products p
JOIN brands b ON b.brand_id = p.brand_id
JOIN categories c ON c.category_id = p.category_id
ON CONFLICT (product_id)DOUPDATESET card_json = EXCLUDED.card_json;-- GIN index for JSON queries if neededCREATEINDEX idx_product_cards_gin ON product_cards USING GIN (card_json);Altyapı:
Senkron seçenekleri: ürün/marka/kategori değişikliklerinde olay güdümlü yeniden kurma veya yakın zamanda güncellenen ürünler için sık toplu yenileme.
Bir senkron stratejisi seçme (hızlı rehber)
Tetikleyiciler: Anlıktırlar ve işlemsel tutarlılık sağlarlar. Genel olarak küçük yayılım ve düşük yazma hacmi için iyidirler; ancak aşırı kullanılırsa yazma gecikmesini artırabilirler.
Uygulama çift yazımı: Uygulama hem kaynağa hem denormalizasyona yazar. Basittir ama daha risklidir. Bunu idempotent yeniden denemeler ve outbox/CDC kalıplarıyla hafifletebilirsiniz.
CDC → işçi: Bu senk stratejisi, değişiklikleri eşzamansız olarak yaydığı için güvenilir ve ölçeklenebilirdir. Sonunda tutarlılığın kabul edilebilir olduğu durumlar için idealdir.
Zamanlanmış işler: Toplamalar ve materialized view’lar için en basit seçenektir. UX toleransınıza uygun yenileme pencereleri seçin.
Çözümünüze mutlaka gözlemlenebilirlik katmanını dahil etmenizi öneririm. Değişikliklerin doğru şekilde işlenip çoğaltıldığından emin olamamak kadar kötü bir şey yoktur. Bu aynı zamanda sapma kontrollerinizi ve backfill betiklerinizi hesaplamanıza da yardımcı olur.
Adım Adım Denormalizasyon Nasıl Uygulanır
Her zamanki gibi, veritabanınızla oynamak oldukça risklidir. Veritabanınızı çoğaltmanızı ve önce denormalizasyonu replika üzerinde uygulamayı denemenizi şiddetle tavsiye ederim; böylece her şeyin beklediğiniz gibi davrandığından emin olursunuz. Bir geliştirme ortamınız varsa, daha da iyi!
Süreç oldukça basit: ölç → mümkün olduğunca az değiştir → doğru tut → doğrula → izle → yinele.
1) Profilleyin ve başarı hedefi koyun
Sorgunun tam metnini (parametrelerle) ve sıklığını yakalayın.
Bir temel değer kaydedin: EXPLAIN (ANALYZE, BUFFERS) planı, p95 gecikmesi, CPU/IO, taranan satırlar.
Bir başarı kriteri üzerinde anlaşın (örneğin “p95 < 120 ms ve yazma maliyeti ≤1,2×”).
Tercihen büyük bir projeksiyon yerine tek bir yinelenen sütun veya tek bir özet tablo deneyin.
Yeni şekli taslaklayın:
Yinelenen sütunlar? (hangileri, neden)
Toplama tablosu veya materialized view? (tane, anahtarlar, yenileme penceresi)
Tutarlılık modeline karar verin:
Güçlü (tetikleyici/işlemsel) vs sonunda (CDC/iş/materialized view yenilemesi).
Yazma amplifikasyonu bütçesini yazın (olay başına kabul edilebilir ek yazma sayısı).
3) Senkron yolunu tasarlayın
Tetikleyiciler (güçlü tutarlılık, küçük yayılım).
CDC/outbox → işçi (iyi ölçeklenir, sonunda tutarlılık):
Uygulama, aynı işlemde kaynak tablolara ve outbox’a yazar.
Bir işçi, outbox’ı okur ve denormalize hedefi idempotent olarak günceller.
Zamanlanmış işler / MV yenilemesi (toplamalar için harika):
Yenileme temposunu, pencerelerini ve backfill stratejisini tanımlayın.
İdempotentlik pazarlıksızdır. Güncellemeler güvenle tekrarlanabilir olmalıdır (ör. belirleyici yeniden hesaplamayla UPSERT). Buna güvenin; sonra kendinize teşekkür edeceksiniz.
4) Oluşturun, backfill yapın ve doğrulayın
Yapıları oluşturun (tablolar, sütunlar, indeksler, tetikleyiciler/işler) ancak uygulamayı değiştirmeyin.
Gerçek kaynaktan backfill yapın.
Eşdeğerliği değişmezlerle doğrulayın:
-- Example parity check: orders_projection vs live joinSELECTCOUNT(*)AS mismatches
FROM orders_projection p
JOIN orders o ON o.order_id = p.order_id
JOIN customers c ON c.customer_id = o.customer_id
WHERE p.customer_name <> c.name
OR p.total_amount <>(SELECTSUM(oi.quantity * oi.unit_price)FROM order_items oi WHERE oi.order_id = o.order_id
);Altyapı:
4. Uyumsuzlukları düzeltin; sıfıra inene (veya üzerinde anlaşılan hata bütçesine) kadar yeniden çalıştırın.
5) Güvenle devreye alın
Uygulama değişikliğini bir özellik bayrağı veya kanarya ile yayınlayın (ör. trafiğin %10’u denormalize yoldan okur).
Gölge okumalar çalıştırın: eski sonucu arka planda hesaplayın ve isteklerin bir alt kümesi için hash/toplamları karşılaştırın.
Hızlı bir geri alma mekanizması tutun (anında normalleştirilmiş yola geri dönün).
6) Doğru şeyleri izleyin
Seçtiğiniz gözlemlenebilirlik aracında küçük bir pano oluşturun:
Okuma: p50/p95 gecikmesi, okunan satırlar, arabellek isabetleri, sorgu planı istikrarı.
Yazma: ek yazma süresi, tetikleyici/iş hataları, kuyruk gecikmesi (CDC), MV son yenilenme zaman damgası.
Veri kalitesi: sapma sayaçları (günlük eşdeğerlik kontrolleri), backfill edilen satır sayısı, uyumsuzluk yüzdesi.
Sahiplik (ekip/nöbet sistemi) ve bir çalışma kitabı atayın (nasıl yeniden kurulur, backfill yapılır, onarılır). Bu sıklıkla gözden kaçar; ancak bir olay olduğunda olabildiğince hızlı yanıt vermek isteyeceksiniz.
Üç ayda bir yeniden değerlendirin: hâlâ gerekli mi? Hâlâ doğru şekil mi? Basitleştirebilir miyiz? Kararı belgeleyin ve panolara bağlayın.
Denormalizasyonun Artıları ve Eksileri
Ayrıntıları önceki bölümdeki Normalleştirme ve Denormalizasyon tablosunda ele aldık; burada hızlı bir özet var.
Kazançlarınız
Daha hızlı okumalar ve daha öngörülebilir p95/p99 gecikmeleri
Sıcak yollarda daha basit sorgular (daha az join/toplulaştırma)
Okuma başına daha düşük CPU/IO ve daha ucuz analitik sorguları
Operasyonel ek yük (yenilemeler, backfill’ler, izleme)
Değişim yayılımı (şema/mantık güncellemeleri yansıtılmalıdır)
Denormalizasyonu Destekleyen Araçlar ve Teknolojiler
Her veritabanı denormalizasyona aynı şekilde yardımcı olmaz. Bazıları pahalı sorgu sonuçlarını kalıcılaştırmak için birinci sınıf özellikler sunarken, diğerleri işleri, tetikleyicileri veya boru hatlarını kullanarak sizin kendi çözümünüzü geliştirmenizi bekler.
PostgreSQL
Postgres, okuma için optimize edilmiş şekilleri somutlaştırmanın birkaç yolunu sunar.
Materialized view’lar, bir sorgunun sonucunu kalıcılaştırır; böylece okumalar anlıktır. Ne zaman yenileneceğini (zamanlanmış veya isteğe bağlı) siz seçersiniz ve okuyucuların engellenmemesi için eşzamanlı yenileme de yapabilirsiniz.
Hafif kopyalamalar için, üretilen sütunlar ve ifade indeksleri, veritabanının türetilmiş değerleri korumasına ve yaygın filtreleri hızlandırmasına olanak tanır; ek senkron mantığı yazmadan.
Kaynak ile denormalize bir kopya arasında güçlü tutarlılığa ihtiyaç duyduğunuzda, senkronu korumak için tetikleyiciler kullanabilirsiniz (ancak unutmayın, bunlar yazma yolunuza iş ekler!). Olay güdümlü veya toplu yenilemeler için Postgres’i bir zamanlayıcıyla (ör. pg_cron veya harici bir iş çalıştırıcısı) ya da mantıksal çoğaltma/CDC ile eşleştirip değişiklikleri, denormalize tablolarınızı eşzamansız güncelleyen bir işçiye akıtabilirsiniz.
Kendi veritabanlarınızı oluşturup denormalizasyonu test etmek istiyorsanız, PostgreSQL kursumuza göz atın.
SQL Server
SQL Server, motorun görünümü her ekleme/güncelleme/silmede senkron tuttuğu, dolayısıyla okumaların çok hızlı olduğu, adeta sürekli açık materialized view’lar olan indeksli görünümlere ağırlık verir. Öte yandan, artık hem temel tablolar hem görünüm bakımı yazmalara dâhildir; dolayısıyla ağır OLTP iş yükleri yavaşlayabilir.
Bunun dışında, tek satırlık türetilmiş öznitelikler için kalıcı hesaplanan sütunlardan yararlanabilirsiniz.
Büyük ölçekli raporlama için, columnstore indeksleri denormalizasyona bir alternatiftir: büyük veri kümelerini çok verimli şekilde sıkıştırır ve tarar; bu da baştan veri çoğaltma ihtiyacını ortadan kaldırabilir.
Oracle
Oracle’ın Materialized View’ları, yalnızca değişeni güncelleyen değişim günlüklerini kullanan FAST yenileme veya tümünü yeniden inşa etmenin sorun olmadığı COMPLETE yenileme ile olgun bir seçenektir.
Query Rewrite ile, birisi temel tabloları sorguladığında, iyileştirici materialized view’ınızı şeffaf biçimde kullanabilir; böylece ekipler uygulama SQL’ini değiştirmeden hız kazanır. Her zamanki gibi, daha hızlı okumalar bir yerde ekstra iş demektir: günlükleri korumak ve görünüm yenilemek, yazma ve operasyonel ek yükü artırır.
MySQL / MariaDB
MySQL yerel materialized view sunmaz; bu nedenle denormalizasyon genellikle fiziksel tablolar artı bunları güncel tutmak için zamanlanmış işler veya tetikleyicilerle inşa edilir.
Üretilen sütunlar, basit türetilmiş değerlerde yardımcı olur. Bu, öngörülebilir panolar ve özetler için iyi çalışır; ancak yine de yenileme temposu ve tetikleyici karmaşıklığı konusunda kasıtlı olmak gerekir; kritik yazma yollarını yanlışlıkla vergilendirmemek için. Birçok ekip bunu okuma replikalarıyla birleştirerek raporlama sorgularını tamamen devre dışı bırakır.
Snowflake / BigQuery (analitik katman)
Sütunlu depolar, geniş, tarama dostu, denormalize veriler için inşa edilmiştir. Genellikle içine yardımcı öznitelikler gömülmüş geniş olgu tabloları modelleyip, ardından tarananı budamak için bölümlendirme/kümelendirmeden yararlanırsınız.
Her iki platform da, OLTP’ye dokunmadan toplamaları taze tutmak için materialized view’lar ve zamanlanmış görevleri/sorguları destekler. Ölçekte çok öngörülebilir, ucuz okumalar için depolama ve yenileme maliyetlerini takas edersiniz. Gösterge tabloları ve BI için mükemmeldir.
Bu, PostgreSQL kursumuzdan biraz daha ileri düzeydir; bu nedenle veritabanlarında zaten rahatsanız Snowflake ile veri modellemeye giriş kursumuzu deneyebilirsiniz.
dbt (modelleme ve orkestrasyon)
dbt, denormalize analitik için “kod olarak altyapı” katmanı olarak bilinir. Modelleri bir kez tanımlar, bir somutlaştırma (tablo, görünüm, artımlı) seçer ve dbt’nin kurulumları, bağımlılıkları ve testleri yönetmesine izin verirsiniz. Yalnızca yeni verilerin eklenip birleştirilmesine ihtiyaç duyan özet tablolar için artımlı modeller özellikle faydalıdır. En sevdiğim araçlardan biridir!
CDC & veri boru hatları
Uygulama değişikliklerinin denormalize yapılara güvenilir biçimde akmasını istediğinizde, Change Data Capture başvurulacak yöntemdir. Debezium (kendi barındırılan) gibi araçlar veya yönetilen bağlayıcılar (Fivetran, Airbyte), OLTP’den satır düzeyindeki değişiklikleri, projeksiyon tablolarını, sayaçları veya toplamaları güncelleyen işçilere veya depolara akıtır. Bu genellikle sonunda tutarlıdır (gösterge tabloları ve akışlar için harika) ve her şeyi tetikleyicilere sıkıştırmaktan çok daha iyi ölçeklenir. Güncellemeleri idempotent yapın, gecikmeyi izleyin ve geç ya da kaçırılan olaylar için bir backfill yolu tutun.
Temel Çıkarımlar & Son Düşünceler
Denormalizasyon, normalleştirilmiş bir tasarımın üzerine katmanlanan pragmatik bir optimizasyondur; onu reddetmek değildir. Belirli ve iyi anlaşılan yollarda daha hızlı, daha basit okumalar için depolamayı ve yazma karmaşıklığını takas edersiniz.
Unutmayın:
Normalleştirilmiş başlayın. Yalnızca adı konmuş, ölçülmüş bir sorgunun ihtiyaç duyduğu yerlerde denormalizasyon kullanın.
Önce daha ucuz çözümleri tüketin (indeksler, sorgu yeniden yazımları, önbellekler, replikalar, OLAP).
Karşılığını veren en küçük denormalizasyonu seçin (tek bir yinelenen sütun, küçük bir toplam veya bir materialized view).
Tazelik konusunda net olun (güçlü ve sonunda tutarlılık) ve idempotent bir senkron inşa edin.
Öncesi/sonrası ölçün, sapmayı izleyin ve bir geri alma anahtarını hazır tutun.
Normalleştirilmiş tabloları gerçek kaynağı olarak görün! Denormalize parçalar, yeniden inşa edebileceğiniz okuma için optimize kopyalardır.
Yazar
Marie Fayard
İlk prototipten ürün-pazar uyumuna ve ötesine kadar erken aşama girişimleri büyütme konusunda uzmanlaşmış, ürün odaklı bir teknik liderim. İnsanların teknolojiyi nasıl kullandığına dair bitmeyen bir merakım var ve kurucularla ile disiplinler arası ekiplerle yakın çalışarak cesur fikirleri hayata geçirmeyi seviyorum. Ürün inşa etmediğim zamanlarda ya dünyanın yeni köşelerinde ilham peşinde koşuyor ya da yoga stüdyosunda stres atıyorum.
Algoritmalar, sistem tasarımı ve davranışsal senaryoları kapsayan bu temel sorularla teknik mülakat sürecine hakim olun. Uzman cevapları, kod örnekleri ve kanıtlanmış hazırlık stratejileri edinin.
Git deponuzu temiz tutmak için .gitignore’u nasıl kullanacağınızı öğrenin. Bu eğitim; temelleri, yaygın kullanım durumlarını ve başlamanıza yardımcı olacak pratik örnekleri kapsar!
I’ve, normalleştirme ve bunun veri bütünlüğü için neden bu kadar güvenilir bir temel olduğuna dair çok şey yazdım. Kendimle çelişmiyorum. İyi normalleştirilmiş bir şemanın çoğu işlemsel sistem için doğru başlangıç noktası olduğuna hâlâ inanıyorum. Bu makale, okuma performansının katı normal forma göre daha önemli olduğu çok belirli durumlarda, bu saflığa bilinçli olarak karşı çıkma seçimini ele alıyor.
Denormalizasyon, normalleştirmeyi atlamak değildir. Gerçek sorgular, gerçek kullanıcılar ve gerçek SLA’ler size join’lerin ve anlık hesaplamaların çok yavaş veya çok maliyetli olduğunu söylediğinde, normalleştirilmiş bir modele uyguladığınız bir performans optimizasyonudur. Pratikte daha hızlı okumalar ve daha basit sorgular için daha fazla depolama, daha karmaşık yazmalar ve ek tutarlılık çalışmasıyla takas yaparsınız.
Bu makalede denormalizasyonun nerede faydalı olduğunu, nerede olmadığını ve SQL veritabanlarında nasıl güvenle uygulanacağını göstereceğim. Amaç, iyi tasarımı terk etmek değil; iş yükü haklı çıkardığında onun üzerine kestirme yolları katmanlamak.
Veritabanlarında Denormalizasyon Nedir?
Normalleştirme ve veritabanı tasarımına aşinaysanız, kısa cevap şöyle:
Denormalizasyon, daha hızlı okumalar ve daha basit sorgular için, önceden normalleştirilmiş bir şemaya kasıtlı olarak yinelenen veri ekleme eylemidir. Bu, iyi modellemeyi atlamak için bir bahane değil, hedefli bir performans optimizasyonudur!
Veritabanı tasarımında nispeten yeniyseniz, “normalleştirilmiş”, “denormalize” ve “normalleştirilmemiş” kavramlarını açmak faydalı olabilir. Bu üç terimi çevrimiçi ortamda sık duyarsınız ve birbirine karıştırmamak önemlidir.
Normalleştirilmiş: Veriler, yinelenmeyi en aza indiren ve bütünlüğü koruyan iyi yapılandırılmış tablolara bölünür (bkz. 3NF/BCNF).
Denormalize: Normalleştirilmiş modelin üzerine, yaygın okumaları hızlandırmak için ekstra sütunlar, önceden hesaplanmış değerler veya önceden birleştirilmiş tablolarla seçici yinelenmeyi yeniden eklersiniz. Denormalizasyonda tek bir gerçek kaynağını (normalleştirilmiş tablolar) korur, ardından gösterge tabloları, ürün listeleri, arama vb. gibi sıcak yollar için daha hızlı bir veya daha fazla temsili sürdürürsünüz. Depolama + yazma karmaşıklığını, okuma hızı + daha basit sorgularla takas edersiniz.
Normalleştirilmemiş: Yapısı ve kısıtları düzgün tasarlanmamış ham, ad hoc veya dağınık veri. Burada yaptığımız bu değil.
SQL’de örnek
Normalleştirilmiş ve denormalize modelleme arasındaki farkı canlandırmanıza yardımcı olacak küçük bir SQL örneği:
Normalleştirilmiş modelleme
Bir raporlama yolu için denormalize
Bu örnekte, customers tablosunu gerçek kaynağı olarak tutar ve
orders.customer_name / orders.customer_tieralanlarının senkron kalmasını sağlarsınız (örneğin, tetikleyici, CDC işi veya zamanlanmış bir backfill ile). Aynı gerçek, iki temsil; her biri farklı bir iş için optimize edilmiştir.Denormalizasyon kötü tasarım mıdır?
Denormalizasyonun sadece kötü tasarımın bir ürünü olduğuna dair yaygın bir yanlış kanı var. Ölçülü, kapsamı belirli ve bakımı yapılırsa durum böyle değildir. Evet, denormalizasyon sıklıkla daha yüksek normal formları ihlal eder (zaten amacı budur), ancak bu kasıtlıdır ve tutarlılığı korumaya yönelik bir planla desteklenir. Kötü tasarım, normalleştirmeyi tamamen atlamak ya da bir senkronizasyon stratejisi olmadan gelişi güzel yinelenme serpiştirmektir.
Veritabanı tasarımı hakkında daha fazla bilgi edinmek isterseniz, konuyla ilgili başlangıç kursumuz iyi bir başlangıç noktasıdır!
Normalleştirme ve Denormalizasyon
İşte yan yana bir karşılaştırma. Kapsamlı değildir ancak her yaklaşımın takaslarını anlamanıza yardımcı olur.
Boyut
Normalleştirme
Denormalizasyon
Birincil amaç
Bütünlük, minimum yinelenme, kolay & doğru güncellemeler
Daha hızlı okumalar, sıcak yollarda daha basit sorgular
En uygun
Sık yazma/güncelleme yapılan OLTP sistemleri
Okuma ağırlıklı gösterge tabloları, arama/listeleme sayfaları, raporlama/analitik
Okuma performansı
Sıklıkla join gerektirir, doğru indekslerle iyidir
Daha az join, çok daha hızlı ve öngörülebilir olabilir
Yazma karmaşıklığı
Basit: tek gerçek kaynağı
Daha yüksek: yinelenen kopyalar veya özetlerin güncellenmesi/senkronu gerekir
Depolama
Sade
Daha büyük (ek sütunlar/tablolar, önceden hesaplanmış görünümler)
Veri bütünlüğü
Tasarım gereği güçlü (3NF/BCNF kısıtları)
Sapmayı önlemek için mekanizmalar gerektirir (tetikleyiciler, CDC, işler)
Değişim hızı
Sütun yeniden adlandırma/güncellemeleri yerelleştirilmiştir
Değişiklikler çoğaltılmış veriler boyunca yayılabilir
Operasyonel ek yük
Daha düşük: daha az hareketli parça
Daha yüksek: yenileme politikaları, backfill’ler, izleme
Arıza biçimleri
N+1 sorgular, yavaş join’ler, eksik indeksler
Bayat veri, tutarsızlık, yazma amplifikasyonu
Şema evrimi
Öngörülebilir, refactor-dostu
Yinelenmiş temsiller için geçiş planları gerekir
Tipik örnekler
Siparişler, müşteriler, işlemler
Önceden join’lenmiş ürün listeleri; toplu satış tabloları; materialized/indeksli görünümler
Neden ve Ne Zaman Denormalize Edilir
Gerçek kullanıcılar ve gerçek sorgular, join’ler, toplulaştırmalar veya tekrarlanan aramalar nedeniyle tıkandığında denormalizasyon gerçekten faydalı hâle gelir. İndeksleme, sorgu ayarı ve önbelleklemenin yeterli olmadığını doğruladıktan sonra, öngörülebilir erişim desenlerinde hızlı okumalar için denormalizasyona yönelebilirsiniz.
Denormalizasyonun yardımcı olabileceği durumlar
Klasik kullanım alanları
Ne zaman denormalize etmemeli ve önce başka bir şey kullanmalı
Denormalizasyonu, size en az ek operasyonel yükle en fazla okuma hızını sağladığı yerlerde ve yalnızca daha ucuz çözümleri elediğinizde kullanmak istersiniz.
Denormalizasyonun şu an için tam olarak ihtiyacınız olan şey olmadığını fark ettiyseniz, bekleyin! Bir sonraki bölümde denormalizasyona alternatifleri keşfedeceğiz.
Denormalizasyona Alternatifler
Denormalizasyon her sorununuzun cevabı değildir. Yinelenme eklemeden önce, motor ve uygulamanızdan alabileceğiniz her şeyi zorlamalısınız. Bu düzeltmelerin bakımı daha ucuzdur ve sıklıkla aynı kazanımları sağlar.
1) İndeksleme
Bileşik/kaplayan indeksler: SQL sorgularınızda, filtre sütunlarını önce, ardından
GROUP BY/ORDER BYsütunlarını koyun. Motorun sorguyu yalnızca indeksten sunabilmesi için seçili liste sütunlarını dahil ediyorum.Filtreli/kısmi indeksler: Yalnızca sıcak dilimi indeksleyin (ör.
status = 'ACTIVE'); indeksi küçük ve hızlı tutar.İfade/fonksiyonel indeksler:
LOWER(email)veyadate_trunc('day', created_at)üzerinde indeks oluşturup hesaplanmış taramalardan kaçının.2) Sorgu ayarı & sayfalama
SELECT *kullanmaktan kaçının. Yalnızca görüntülediğinizi alın.Yalnızca varlık kontrolü gerektiğinde gereksiz join’leri
EXISTS/SEMIile değiştirin.Koşulları aşağı itin: erken filtreleyin, geç toplulaştırın.
Stabil, hızlı kaydırma için anahtar kümesi sayfalama (
WHERE created_at < ? ORDER BY created_at DESC LIMIT 50) kullanın.3) Önbellekleme
4) Okuma replikaları
5) Bölümlendirme & budama
6) Sütunlu/OLAP depoları
7) ORM hijyeni
8) Hesaplanan/üretilen sütunlar (VT tarafından yönetilen)
Türetilmiş değerleri (ör.
price_with_tax) üretilen/hesaplanan sütunlar veya ifade indeksleri olarak veritabanına yönettirin. Böylece uygulama seviyesinde senkron mantığı yazmadan hızlı okumalar elde edersiniz.Denormalizasyon Teknikleri
Normalleştirilmiş bir modele kontrollü yinelenme eklemenin en yaygın yolları burada. Her biri için ne yaptığını, ne zaman kullanılacağını ve nasıl senkron tutulacağını göstereceğim.
Kurulum: çekirdekte customers, orders, order_items, products bulunan normalleştirilmiş bir yapı varsayın.
1) Düzleştirilmiş / önceden join’lenmiş “projeksiyon” tabloları
Ne: Hızlı okumalar için ihtiyaç duyduğunuz sütunları önceden join’leyen bir tablo oluşturun (ör. ürün listesi veya siparişler panosu).
Ne zaman: Sıcak yolunuz 3 ila 5 tabloyu öngörülebilir biçimlerde join’liyorsa.
Nasıl (PostgreSQL):
Senkron seçenekleri:
2) Yinelenen sütunlar (birkaç özniteliği kopyala)
Ne: Sık okunan birkaç özniteliği, join’lerden kaçınmak için başka bir tabloya çoğaltın (ör. orders.customer_name).
Ne zaman: Yalnızca 1 veya 2 değere ihtiyacınız varsa ve tam bir projeksiyon istemiyorsanız.
Nasıl (PostgreSQL tetikleyici örneği):
Takas: Okumalar basittir, ancak bir müşteri güncellendiğinde yazmalar yayılarak artar.
3) Türetilmiş öznitelikler (hesaplanan değerleri sakla)
Ne: Normalde anlık hesapladığınız değerleri kalıcılaştırın (ör.
price_with_tax,latest_comment_at,item_count).Ne zaman: Hesaplama sürekli yapılıyor ve determinizm var.
İki yaygın kalıp:
4) Toplam/özet tabloları
Ne: Günlük gelir, günlük aktif kullanıcılar, kategori başına siparişler gibi rollup’ları önceden hesaplayın.
Ne zaman: Gösterge tabloları aynı group-by’ları tekrar ediyor ve ham taramalar pahalı.
Senkron seçenekleri: zamanlanmış iş (cron/dbt), akış güncellemeleri (CDC) veya olay güdümlü toplayıcılar.
5) Materialized view’lar (kalıcı sorgu sonuçları)
Ne: Pahalı bir sorgunun çıktısını indeksleyebileceğiniz fiziksel bir tablo olarak saklayın.
Ne zaman: Sonuç kümesi pahalı, nispeten stabil ve yenileme pencerelerini tolere edebiliyorsanız.
Takas: Postgres’te artımlı yenileme yerel değildir; bu nedenle yenileme temposunu yönetirsiniz. Yine de, dbt veya özel “yalnızca ekle + pencereli yeniden kur” kalıpları kullandığınızda yeterince kolaydır.
6) Önceden hesaplanmış JSON “belge” projeksiyonları
Ne: API/UI ihtiyaçlarınıza uyan denormalize bir JSON yığını (ör. ürün kartı) saklayın ve yanında hedefli JSON indeksleri bulundurun.
Ne zaman: API’niz aynı şekli tekrarlı sunuyorsa ve tek okumayla iş yapmak istiyorsanız.
Senkron seçenekleri: ürün/marka/kategori değişikliklerinde olay güdümlü yeniden kurma veya yakın zamanda güncellenen ürünler için sık toplu yenileme.
Bir senkron stratejisi seçme (hızlı rehber)
Çözümünüze mutlaka gözlemlenebilirlik katmanını dahil etmenizi öneririm. Değişikliklerin doğru şekilde işlenip çoğaltıldığından emin olamamak kadar kötü bir şey yoktur. Bu aynı zamanda sapma kontrollerinizi ve backfill betiklerinizi hesaplamanıza da yardımcı olur.
Adım Adım Denormalizasyon Nasıl Uygulanır
Her zamanki gibi, veritabanınızla oynamak oldukça risklidir. Veritabanınızı çoğaltmanızı ve önce denormalizasyonu replika üzerinde uygulamayı denemenizi şiddetle tavsiye ederim; böylece her şeyin beklediğiniz gibi davrandığından emin olursunuz. Bir geliştirme ortamınız varsa, daha da iyi!
Süreç oldukça basit: ölç → mümkün olduğunca az değiştir → doğru tut → doğrula → izle → yinele.
1) Profilleyin ve başarı hedefi koyun
2) İşe yarayan en küçük denormalizasyonu seçin
3) Senkron yolunu tasarlayın
İdempotentlik pazarlıksızdır. Güncellemeler güvenle tekrarlanabilir olmalıdır (ör. belirleyici yeniden hesaplamayla
UPSERT). Buna güvenin; sonra kendinize teşekkür edeceksiniz.4) Oluşturun, backfill yapın ve doğrulayın
4. Uyumsuzlukları düzeltin; sıfıra inene (veya üzerinde anlaşılan hata bütçesine) kadar yeniden çalıştırın.
5) Güvenle devreye alın
6) Doğru şeyleri izleyin
Seçtiğiniz gözlemlenebilirlik aracında küçük bir pano oluşturun:
7) İşletin & yineleyin
Denormalizasyonun Artıları ve Eksileri
Ayrıntıları önceki bölümdeki Normalleştirme ve Denormalizasyon tablosunda ele aldık; burada hızlı bir özet var.
Kazançlarınız
Maliyetleri
Denormalizasyonu Destekleyen Araçlar ve Teknolojiler
Her veritabanı denormalizasyona aynı şekilde yardımcı olmaz. Bazıları pahalı sorgu sonuçlarını kalıcılaştırmak için birinci sınıf özellikler sunarken, diğerleri işleri, tetikleyicileri veya boru hatlarını kullanarak sizin kendi çözümünüzü geliştirmenizi bekler.
PostgreSQL
Postgres, okuma için optimize edilmiş şekilleri somutlaştırmanın birkaç yolunu sunar.
Materialized view’lar, bir sorgunun sonucunu kalıcılaştırır; böylece okumalar anlıktır. Ne zaman yenileneceğini (zamanlanmış veya isteğe bağlı) siz seçersiniz ve okuyucuların engellenmemesi için eşzamanlı yenileme de yapabilirsiniz.
Hafif kopyalamalar için, üretilen sütunlar ve ifade indeksleri, veritabanının türetilmiş değerleri korumasına ve yaygın filtreleri hızlandırmasına olanak tanır; ek senkron mantığı yazmadan.
Kaynak ile denormalize bir kopya arasında güçlü tutarlılığa ihtiyaç duyduğunuzda, senkronu korumak için tetikleyiciler kullanabilirsiniz (ancak unutmayın, bunlar yazma yolunuza iş ekler!). Olay güdümlü veya toplu yenilemeler için Postgres’i bir zamanlayıcıyla (ör. pg_cron veya harici bir iş çalıştırıcısı) ya da mantıksal çoğaltma/CDC ile eşleştirip değişiklikleri, denormalize tablolarınızı eşzamansız güncelleyen bir işçiye akıtabilirsiniz.
Kendi veritabanlarınızı oluşturup denormalizasyonu test etmek istiyorsanız, PostgreSQL kursumuza göz atın.
SQL Server
SQL Server, motorun görünümü her ekleme/güncelleme/silmede senkron tuttuğu, dolayısıyla okumaların çok hızlı olduğu, adeta sürekli açık materialized view’lar olan indeksli görünümlere ağırlık verir. Öte yandan, artık hem temel tablolar hem görünüm bakımı yazmalara dâhildir; dolayısıyla ağır OLTP iş yükleri yavaşlayabilir.
Bunun dışında, tek satırlık türetilmiş öznitelikler için kalıcı hesaplanan sütunlardan yararlanabilirsiniz.
Büyük ölçekli raporlama için, columnstore indeksleri denormalizasyona bir alternatiftir: büyük veri kümelerini çok verimli şekilde sıkıştırır ve tarar; bu da baştan veri çoğaltma ihtiyacını ortadan kaldırabilir.
Oracle
Oracle’ın Materialized View’ları, yalnızca değişeni güncelleyen değişim günlüklerini kullanan
FASTyenileme veya tümünü yeniden inşa etmenin sorun olmadığıCOMPLETEyenileme ile olgun bir seçenektir.Query Rewrite ile, birisi temel tabloları sorguladığında, iyileştirici materialized view’ınızı şeffaf biçimde kullanabilir; böylece ekipler uygulama SQL’ini değiştirmeden hız kazanır. Her zamanki gibi, daha hızlı okumalar bir yerde ekstra iş demektir: günlükleri korumak ve görünüm yenilemek, yazma ve operasyonel ek yükü artırır.
MySQL / MariaDB
MySQL yerel materialized view sunmaz; bu nedenle denormalizasyon genellikle fiziksel tablolar artı bunları güncel tutmak için zamanlanmış işler veya tetikleyicilerle inşa edilir.
Üretilen sütunlar, basit türetilmiş değerlerde yardımcı olur. Bu, öngörülebilir panolar ve özetler için iyi çalışır; ancak yine de yenileme temposu ve tetikleyici karmaşıklığı konusunda kasıtlı olmak gerekir; kritik yazma yollarını yanlışlıkla vergilendirmemek için. Birçok ekip bunu okuma replikalarıyla birleştirerek raporlama sorgularını tamamen devre dışı bırakır.
Snowflake / BigQuery (analitik katman)
Sütunlu depolar, geniş, tarama dostu, denormalize veriler için inşa edilmiştir. Genellikle içine yardımcı öznitelikler gömülmüş geniş olgu tabloları modelleyip, ardından tarananı budamak için bölümlendirme/kümelendirmeden yararlanırsınız.
Her iki platform da, OLTP’ye dokunmadan toplamaları taze tutmak için materialized view’lar ve zamanlanmış görevleri/sorguları destekler. Ölçekte çok öngörülebilir, ucuz okumalar için depolama ve yenileme maliyetlerini takas edersiniz. Gösterge tabloları ve BI için mükemmeldir.
Bu, PostgreSQL kursumuzdan biraz daha ileri düzeydir; bu nedenle veritabanlarında zaten rahatsanız Snowflake ile veri modellemeye giriş kursumuzu deneyebilirsiniz.
dbt (modelleme ve orkestrasyon)
dbt, denormalize analitik için “kod olarak altyapı” katmanı olarak bilinir. Modelleri bir kez tanımlar, bir somutlaştırma (tablo, görünüm, artımlı) seçer ve dbt’nin kurulumları, bağımlılıkları ve testleri yönetmesine izin verirsiniz. Yalnızca yeni verilerin eklenip birleştirilmesine ihtiyaç duyan özet tablolar için artımlı modeller özellikle faydalıdır. En sevdiğim araçlardan biridir!
CDC & veri boru hatları
Uygulama değişikliklerinin denormalize yapılara güvenilir biçimde akmasını istediğinizde, Change Data Capture başvurulacak yöntemdir. Debezium (kendi barındırılan) gibi araçlar veya yönetilen bağlayıcılar (Fivetran, Airbyte), OLTP’den satır düzeyindeki değişiklikleri, projeksiyon tablolarını, sayaçları veya toplamaları güncelleyen işçilere veya depolara akıtır. Bu genellikle sonunda tutarlıdır (gösterge tabloları ve akışlar için harika) ve her şeyi tetikleyicilere sıkıştırmaktan çok daha iyi ölçeklenir. Güncellemeleri idempotent yapın, gecikmeyi izleyin ve geç ya da kaçırılan olaylar için bir backfill yolu tutun.
Temel Çıkarımlar & Son Düşünceler
Denormalizasyon, normalleştirilmiş bir tasarımın üzerine katmanlanan pragmatik bir optimizasyondur; onu reddetmek değildir. Belirli ve iyi anlaşılan yollarda daha hızlı, daha basit okumalar için depolamayı ve yazma karmaşıklığını takas edersiniz.
Unutmayın: