Denormalisasi di DynamoDB
Datang dari SQL, denormalisasi terdengar seperti dosa — data terduplikasi, tidak ada single source of truth. Di DynamoDB itulah intinya. Tidak ada join, jadi Anda menyalin data terkait ke item yang membutuhkannya dan membacanya kembali dalam satu shot.
Apa itu denormalisasi di DynamoDB?
Denormalisasi di DynamoDB berarti menyalin data terkait ke item yang membacanya, sehingga satu query mengembalikan semuanya dalam satu shot. Karena DynamoDB tidak punya join, Anda pre-join saat write alih-alih menjahit tabel bersama saat baca. Trade-off-nya adalah staleness — hanya duplikasikan nilai yang jarang berubah.
- Tanpa join berarti Anda pre-join saat write. Simpan nilai terkait pada item yang membacanya, agar query tidak pernah butuh lookup kedua.
- Dua rasa. Embed data nested di atribut kompleks pada satu item, atau duplikasi nilai lintas banyak item.
- Jebakannya adalah staleness. Saat sumber berubah, setiap salinan salah sampai Anda fan-out update. Hanya duplikasikan nilai yang jarang berubah.
- Ia membeli baca, bukan tulis. Anda menukar write yang lebih banyak (dan lebih hati-hati) untuk baca murah single-request.
Mengapa tidak ada join untuk diandalkan
JOIN relasional menyusun ulang baris dinormalisasi saat baca. DynamoDB tidak
punya join — Query membaca satu dan
menyerahkan tepat apa yang tersimpan di sana. Tidak ada yang menjahit dua tabel
untuk Anda. (Di jalur baca produksi, bagaimanapun — untuk audit ad-hoc atau cek
drift, SQL Workbench DynoTable menjalankan JOIN
sungguhan atas DynamoDB di sisi klien.)
Jadi data harus sudah dibentuk untuk baca. Jika layar butuh post dan nama authornya, nama itu harus hidup di suatu tempat yang sudah disentuh baca post. Paper Amazon Dynamo 2007 membuat trade ini eksplisit: jatuhkan fitur relasional untuk mendapat baca yang prediktabel pada skala — trade yang sekarang DynamoDB sampaikan sebagai baca single-digit-millisecond.
Pattern 1 — embed dengan atribut kompleks
Atribut DynamoDB bisa menampung map dan list nested, bukan hanya skalar. Jadi satu bentuk umum denormalisasi adalah menyumbat objek child langsung di dalam item parentnya alih-alih memberinya item sendiri.
Post dengan tag dan snapshot author kecil, semuanya di satu item:
| PK | SK | author | tags |
|---|---|---|---|
| POST#9f3 | META | {id: U#12, name: "Mara Vance"} | ["dynamodb","aws"] |
Satu GetItem mengembalikan post, tag, dan blok author bersama. Tanpa baca
kedua. Ini bagus untuk data yang dimiliki parent dan terbatas ukurannya —
segenggam tag, satu snapshot author.
Satu item DynamoDB maksimal 400 KB, termasuk nama dan nilai atribut (Service Quotas). Embed list tanpa batas (setiap komentar pada post viral) dan Anda akan melampauinya.
Pattern 2 — duplikasi nilai lintas item
Kasus blog adalah yang textbook. Anda mendaftar post dan ingin tiap baris menampilkan display name author — tetapi tidak ingin baca kedua per post untuk mengambilnya.
Jadi Anda menulis nama author ke setiap item post saat post dibuat:
| 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" |
atas post (mis. GSI1PK = "POST", atau di-key by author) merender
seluruh daftar — title dan author — tanpa lookup per-baris. begins_with pada
partition key bukan hal; Query butuh equality partition key, jadi dengan
partition key per-post daftar datang dari GSI, bukan Query atas POST#. Nama
author didenormalisasi: salinan kanonis hidup di USER#12, dan setiap post
membawa salinannya sendiri.
Trade-nya tepat di situ. Anda mengubah baca N+1 menjadi satu baca, dengan biaya
memegang "Mara Vance" di N+1 tempat.
Embed vs. duplicate — mana
| Embed (atribut kompleks) | Duplicate (salin lintas item) | |
|---|---|---|
| Bentuk | child nested di dalam parent | nilai sama pada banyak item |
| Terbaik untuk | data terbatas milik parent | nilai bersama yang banyak item tampilkan |
| Baca | satu GetItem | satu Query |
| Biaya update | rewrite satu item parent | fan-out ke setiap salinan |
| Risiko ukuran | langit-langit item 400 KB | tidak ada per item |
Jangkau embed saat child hanya pernah muncul bersama parentnya. Jangkau duplicate saat banyak item independen perlu menampilkan nilai bersama yang sama.
Jebakan: salinan basi
Mara mengganti namanya menjadi "Mara V." Anda meng-update USER#12. Setiap item
post masih bilang "Mara Vance" sampai Anda pergi memperbaikinya.
Jadi meng-update nilai yang diduplikasi adalah fan-out write, bukan one-liner. Anda Query setiap item terdampak dan rewrite masing-masing — idealnya dijaga agar hanya menyentuh baris yang masih memegang nilai lama:
UPDATE POST#9f3
SET authorName = "Mara V."
WHERE authorName = "Mara Vance"
Anda bisa menyusun SET kondisional itu terhadap authorName di
Expression Builder dan menyalin
UpdateExpression serta ConditionExpression yang dihasilkan langsung ke kode.
Fan-out adalah satu write per item. Query GSI ber-key author untuk post author itu, lalu keluarkan update. Urutannya:
Setiap perubahan ke sumber adalah query plus write per salinan. Di DynoTable fan-out mendarat di staging area dulu, sebagai diff per-atribut yang bisa ditinjau per item — Anda melihat setiap salinan yang akan berubah sebelum apa pun di-ship.
Inilah mengapa aturannya hanya duplikasikan nilai yang jarang berubah. Display name, plan tier, label kategori — baik. Counter live atau field yang sering diedit — jangan; fan-out akan memakan Anda hidup-hidup.
Biaya fan-out write
Setiap salinan yang Anda update adalah write terpisah yang ditagih. Di
us-east-1 on-demand, meng-update 50 item post setelah rename author
menelan 50 × WCU — biasanya 1 WCU per KB per item saat tiap baris post
≤ 1 KB. Sisi baca tetap satu Query; sisi tulis berskala dengan berapa
banyak duplikat yang Anda pelihara. Perkirakan kedua jalur di
kalkulator harga.
Kapan normalisasi tetap menang
Jika nilai sering berubah, atau satu item dibaca by pattern yang benar-benar tidak prediktabel, jaga dinormalisasi dan terima baca ekstra. Denormalisasi adalah optimasi untuk access pattern yang diketahui, read-heavy — bukan default untuk diterapkan di mana-mana. Pre-join baca yang benar-benar Anda jalankan, dan biarkan sisanya.
Untuk memutuskan di mana atribut yang diduplikasi ini hidup, model access pattern dulu — lihat single-table design dan, untuk sisi baca dari trade, Query vs Scan.
Unduh DynoTable untuk memeriksa tabel yang didenormalisasi,
melihat salinan mana yang drift, dan menjalankan fan-out update terhadap data
Anda sendiri. SQL Workbench-nya bahkan bisa JOIN
sumber kebenaran terhadap salinan untuk menemukan drift dalam satu query.