DynamoDB'de Denormalizasyon
SQL'den geliyorsan denormalizasyon bir günah gibi gelir — çoğaltılmış veri, tek bir doğruluk kaynağı yok. DynamoDB'de tüm mesele budur. Join yoktur, bu yüzden ilgili veriyi ona ihtiyaç duyan öğenin üzerine kopyalar ve tek seferde geri okursun.
DynamoDB'de denormalizasyon nedir?
DynamoDB'de denormalizasyon, ilgili veriyi onu okuyan öğenin üzerine kopyalamak demektir; böylece tek bir sorgu her şeyi bir seferde döner. DynamoDB'de join olmadığı için okuma zamanında tabloları dikmek yerine yazma zamanında önceden join edersin. Takas bayatlamadır — yalnızca nadiren değişen değerleri çoğalt.
- Join yok demek yazma zamanında önceden join demektir. İlgili değeri onu okuyan öğede sakla ki sorgu asla ikinci bir lookup istemesin.
- İki çeşidi var. İç içe veriyi bir öğede karmaşık bir attribute içine göm, veya bir değeri birçok öğe boyunca çoğalt.
- Tuzak bayatlamadır. Kaynak değişince her kopya, güncellemeyi yayana kadar yanlıştır. Yalnızca nadiren değişen değerleri çoğalt.
- Okuma alır, yazma değil. Ucuz, tek istekli okumalar için daha fazla (ve daha dikkatli) yazma takas edersin.
Neden düşülecek join yoktur
İlişkisel bir JOIN okuma zamanında normalize satırları yeniden birleştirir.
DynamoDB'de join yoktur — bir Query bir
okur ve orada saklananı aynen verir. İki tabloyu senin için diken bir şey yoktur.
(En azından production okuma yolunda — ad-hoc audit veya drift kontrolü için
DynoTable'ın SQL Workbench'i DynamoDB üzerinde istemci
tarafında gerçek bir JOIN çalıştırır.)
Yani veri zaten okuma için şekillendirilmiş olmalıdır. Bir ekran bir post ve yazarının adını istiyorsa, o ad post okumasının zaten dokunduğu bir yerde yaşamalıdır. 2007 Amazon Dynamo makalesi bu takası açık yaptı: ölçekte öngörülebilir okumalar için ilişkisel özellikleri düş — DynamoDB'nin şimdi tek haneli milisaniye okumalar olarak sunduğu takas.
Desen 1 — karmaşık attribute ile göm
DynamoDB attribute'ları yalnızca skaler değil, iç içe map ve list tutabilir. Yani denormalizasyonun yaygın bir biçimi, çocuğa kendi öğesini vermek yerine doğrudan parent öğesinin içine tıkmaktır.
Etiketleri ve küçük bir yazar anlık görüntüsüyle bir post, hepsi bir öğede:
| PK | SK | author | tags |
|---|---|---|---|
| POST#9f3 | META | {id: U#12, name: "Mara Vance"} | ["dynamodb","aws"] |
Tek bir GetItem postu, etiketleri ve yazar bloğunu birlikte döner. İkinci
okuma yok. Bu, parent'a ait ve boyutu sınırlı veri için harikadır — bir avuç
etiket, bir yazar anlık görüntüsü.
Tek bir DynamoDB öğesi, attribute adları ve değerleri dahil 400 KB'ta tıkanır (Service Quotas). Sınırsız bir liste göm (viral bir posttaki her yorum) ve onu aşarsın.
Desen 2 — bir değeri öğeler boyunca çoğalt
Blog vakası ders kitabıdır. Postları listelersin ve her satırın yazarın görünen adını göstermesini istersin — ama onu getirmek için post başına ikinci bir okuma istemezsin.
Bu yüzden post oluşturulurken yazarın adını her post öğesine yazarsın:
| PK | SK | authorId | authorName | title |
|---|---|---|---|---|
| POST#9f3 | META | U#12 | "Mara Vance" | "Modeling 1:N" |
| POST#a71 | META | U#12 | "Mara Vance" | "Sparse GSIs" |
| POST#b04 | META | U#88 | "Lio Tan" | "Query vs Scan" |
Postlar üzerinde bir (diyelim GSI1PK = "POST", veya yazara göre
anahtarlı) tüm listeyi — başlık ve yazar — satır başına lookup olmadan render
eder. Partition key üzerinde begins_with bir şey değildir; bir Query partition-key
eşitliği ister, bu yüzden post başına partition key'lerle liste POST# üzerinde
bir Query'den değil GSI'den gelir. Yazar adı denormalized'dır: kanonik
kopya USER#12'de yaşar ve her post kendi kopyasını taşır.
Takas tam oradadır. N+1 okumayı bir okumaya çevirdin, "Mara Vance"'i N+1 yerde
tutma pahasına.
Göm vs çoğalt — hangisi
| Göm (karmaşık attribute) | Çoğalt (öğeler boyunca kopya) | |
|---|---|---|
| Şekil | çocuk parent içinde iç içe | aynı değer birçok öğede |
| En iyisi | sınırlı, parent'a ait veri | birçok öğenin gösterdiği paylaşılan değer |
| Okuma | bir GetItem | bir Query |
| Güncelleme | tek parent öğeyi yeniden yaz | her kopyaya yayıl |
| Boyut riski | 400 KB öğe tavanı | öğe başına yok |
Çocuk yalnızca parent'ıyla birlikte görünüyorsa göm'e uzan. Birçok bağımsız öğe aynı paylaşılan değeri göstermek zorundaysa çoğalt'a uzan.
Tuzak: bayat kopyalar
Mara kendini "Mara V." olarak yeniden adlandırır. USER#12'yi güncellersin. Her
post öğesi sen düzeltine kadar hâlâ "Mara Vance" der.
Yani çoğaltılmış bir değeri güncellemek tek satır değil, bir fan-out yazmasıdır. Etkilenen her öğeyi sorgular ve her birini yeniden yazarsın — idealde hâlâ eski değeri tutan satırlara yalnızca dokunacak şekilde korunmuş:
UPDATE POST#9f3
SET authorName = "Mara V."
WHERE authorName = "Mara Vance"
authorName'e karşı o koşullu SET'i Expression
Builder'da oluşturup üretilen
UpdateExpression ve ConditionExpression'ı doğrudan koduna kopyalayabilirsin.
Fan-out öğe başına bir yazmadır. O yazarın postları için yazar-anahtarlı GSI'yi sorgula, sonra güncellemeleri yolla. Sıra:
Kaynağa her değişiklik, kopya başına bir sorgu artı bir yazmadır. DynoTable'da fan-out önce staging area'ya düşer — öğe başına incelenebilir attribute diff olarak — bir şey gitmeden önce değişecek her kopyayı görürsün.
Bu yüzden kural yalnızca nadiren değişen değerleri çoğalttır. Bir görünen ad, bir plan tier'ı, bir kategori etiketi — tamam. Canlı bir sayaç veya sık düzenlenen bir alan — yapma; fan-out seni yer bitirir.
Fan-out yazma maliyeti
Güncellediğin her kopya ayrı faturalanan bir yazmadır. us-east-1 on-demand'de
bir yazar yeniden adlandırmasından sonra 50 post öğesini güncellemek
50 × WCU tutar — her post satırı ≤ 1 KB iken tipik olarak öğe başına
1 KB başına 1 WCU. Okuma tarafı bir Query kalır; yazma tarafı tuttuğun
kopya sayısıyla ölçeklenir. Her iki yolu da pricing
calculator'da tahmin et.
Normalizasyonun hâlâ kazandığı yer
Bir değer sık değişiyorsa veya bir öğe gerçekten öngörülemez desenler tarafından okunuyorsa, normalize tut ve ekstra okumayı kabul et. Denormalizasyon bilinen, okuma-ağır erişim desenleri için bir optimizasyondur — her yere uygulanacak bir varsayılan değil. Gerçekten çalıştırdığın okumaları önceden join et, gerisini bırak.
Bu çoğaltılmış attribute'ların nerede yaşayacağına karar vermek için önce erişim desenlerini modelle — bak single-table design ve takasın okuma tarafı için Query vs Scan.
Denormalize bir tabloyu incelemek, hangi kopyaların saptığını bulmak ve fan-out
güncellemesini kendi verine karşı çalıştırmak için DynoTable'ı indir.
SQL Workbench'i hatta drift'i tek sorguda bulmak için
doğruluk kaynağını kopyalara karşı JOIN edebilir.