← semua materi
05:00 Arsitektur · 03 Nov 2026 · 05:01

📚 Topik: Sharding — Memilih Shard Key & Hot Partition Problem

Kategori: Distributed Systems · Level: advanced


🎯 Tujuan Belajar

Setelah sesi ini, keputusan ini jadi lebih baik:

  1. Bisa menilai shard key sebelum data masuk (karena shard key adalah one-way door di banyak sistem).
  2. Bisa mengenali hot partition dari pola akses, bukan dari jumlah data.
  3. Bisa memutuskan kapan sharding tidak perlu — dan opsinya lebih murah.

🧠 Penjelasan Inti

Sharding = partisi horizontal data across node, masing-masing node (shard) punya subset data independen. Tujuan: melewati batas single-node (throughput write, ukuran dataset, blast radius).

Alasan sebenarnya jarang "data kebanyakan". Single Postgres bisa pegang TB dengan indexing benar. Alasan sah sharding:

Kalau alasanmu "tabel sudah 200 juta baris" → itu bukan alasan. Itu alasan partisi + index tuning.

Shard key adalah kontrak. Dia menentukan: routing query, distribusi data, distribusi load, dan apakah cross-shard transaction dibutuhkan. Salah pilih = 6 bulan kemudian kamu menulis migration job yang jalan 3 minggu sambil dua sistem hidup bersamaan.


⚙️ Cara Kerja Internal

Tiga level yang sering dicampur:

logical key  →  hash/range function  →  partition  →  physical node
   user_id         murmur3 % 4096        p_17         node-b

Poin kritis: partisi logis harus jauh lebih banyak dari node fisik (mis. 4096 partition untuk 16 node). Ini yang bikin rebalancing murah — yang dipindah adalah partition, bukan re-hash seluruh keyspace (lihat: consistent hashing, rendezvous hashing).

Dua keluarga fungsi:

Hash-based Range-based
Distribusi Rata secara statistik Mengikuti data
Range scan ❌ fan-out ke semua shard ✅ lokal
Prefix/ordered access ❌ ✅
Hotspot dari sequential key ✅ imun ❌ rentan (timestamp, auto-increment)
Rebalance granularity partition split point

Range sharding dengan monotonic key (timestamp, UUIDv7, snowflake) = selalu hot di ujung kanan. Ini bukan bug implementasi, ini sifatnya. Solusi: prefix acak (2–3 char), atau hash prefix + sort key di dalam partisi.

Kriteria shard key yang benar (empat, harus semua):

  1. Cardinality tinggi — minimal ~10× jumlah partisi. country (200 nilai) dengan 64 shard = distribusi kasar.
  2. Akses merata — bukan cuma jumlah key merata, tapi request rate merata. 1 juta user tapi 1 user = 40% traffic → tetap hot.
  3. Ada di hampir semua query — kalau 80% query tidak menyertakan shard key, 80% query jadi scatter-gather.
  4. Immutable / low-churn — update pada field shard key = delete+insert lintas partisi. Mahal dan sering tidak didukung.

Composite shard key sering jawabannya: (tenant_id, user_id) atau (tenant_id, created_at). Tenant besar bisa di-split lebih lanjut; tenant kecil tetap rapat (locality bagus untuk query per tenant).


🏗️ Pseudocode: deteksi hot partition sebelum produksi

# estimasi load per shard dari histogram akses 7 hari
from collections import Counter

def shard_load(requests, num_partitions, key_fn):
    buckets = Counter()
    for tenant, user, weight in requests:   # weight = req/detik
        p = key_fn(tenant, user) % num_partitions
        buckets[p] += weight

    total = sum(buckets.values())
    mean = total / num_partitions
    peak = max(buckets.values())
    # metrik utama: peak/mean, BUKAN stdev
    return {"peak_over_mean": peak / mean, "hottest": buckets.most_common(3)}

# threshold praktis: >2.0 = desain ulang sekarang
assert shard_load(demo_reqs, 4096, key_fn)["peak_over_mean"] < 2.0

Kenapa peak/mean dan bukan stdev: stdev bilang "ada variasi"; peak/mean bilang "partisi terpanas menerima N× rata-rata" — itu yang langsung diterjemahkan ke throttling.

Aturan kasar: peak/mean > 2 → tolak desain. > 1.5 di trafik puncak (mis. flash sale) → tolak juga. Karena headroom yang kamu hitung di rata-rata akan habis di p99.


⚖️ Trade-off & Kapan JANGAN Dipakai

Jangan shard kalau:

Biaya yang sering dilupakan (bukan cuma ops):

Biaya Dampak
Cross-shard JOIN harus di app layer / scatter-gather, latency = max(shard), bukan sum
Distributed transaction 2PC atau saga; 2PC punya blocking failure mode
Global unique constraint hilang; butuh ID generator terpusat (snowflake, ULID) atau reservation table
Cross-shard aggregation COUNT/SUM jadi map-reduce; dashboard jadi mahal
Rebalancing butuh backfill + dual-write + verifikasi; window nya berhari-hari
Debugging satu request menyentuh 5 node; tracing jadi wajib, bukan opsional

Yang benar-benar tidak bisa ditawar: sekali shard, selamanya kamu punya kompleksitas ini. Tidak ada "un-shard" yang murah.

Resharding reality check (terverifikasi):


🏭 Contoh Produksi & Failure Mode

1. Shard key = tenant_id dengan satu tenant raksasa. Enterprise customer dengan 30% traffic jatuh di satu shard. Shard itu CPU 100%, sisanya idle 8%. Adaptive capacity tidak menolong karena cap per-partisi. Yang terjadi: throttling untuk tenant itu, dan tim lain melihat dashboard hijau — insiden tak terlihat sampai tenant besar komplain. → Fix struktural: (tenant_id, bucket) dengan bucket = hash(user_id) % 16. Tenant kecil bayar overhead routing kecil; tenant besar tersebar.

2. Shard key = timestamp / auto-increment. Kafka-like ingestion, key = created_at. Semua write hari ini ke shard terakhir. Write throughput dibatasi satu node selamanya, dan tidak akan pernah membaik seiring pertumbuhan cluster. Ini arsitektur yang "scalable" di atas kertas dan tidak pernah scale di praktik.

3. Cross-shard query yang tumbuh diam-diam. Mulai dengan 3 query lintas shard. Dua tahun kemudian semua list endpoint pakai filter yang tidak ada shard key-nya → setiap page load = 64 RPC. Latency p50 terlihat OK (paralel), p99 hancur karena tail latency = max dari 64 sampel (lihat: tail-at-scale). Query count tumbuh lebih cepat dari traffic.

4. Resharding tanpa dual-write. Backfill 800 juta row sambil live traffic. Kalau tidak ada dual-write + verifikasi checksum per-partisi, kamu akan menemukan drift 3 minggu kemudian tanpa cara mengetahui baris mana yang salah.

5. Hot partition dari celebrity / viral content. Key = post_id. Satu post viral = semua read ke satu partisi. Cap per partisi terlampaui meski cluster longgar. Solusi kanonik: key salting (post_id + random 0..9, read fan-out 10 partisi, lalu merge) atau cache di depan. Trade-off: write jadi 10× lebih mahal.


⚠️ Miskonsepsi Umum

Miskonsepsi Koreksi
"Hash sharding menyelesaikan hot partition" Hash menyebar key, bukan load. Satu key dengan 50k req/s tetap di satu partisi. Hash hanya menyelesaikan hot partition yang disebabkan key skew, bukan load skew.
"Tambah shard = tambah throughput" Hanya kalau load terdistribusi. Kalau 1 partisi panas, tambah node tidak mengubah apapun — cap per partisi tetap.
"Consistent hashing bikin rebalance otomatis dan murah" Murah dalam jumlah key yang dipindah, tapi tetap butuh backfill data aktual + verifikasi. "Murah" ≠ "gratis".
"Shard key harus unik/primary key" Tidak. Shard key justru sering non-unik (tenant_id) dan itu bagus untuk locality. Yang penting: ada di query dan load-nya rata.
"Kalau butuh cross-shard transaction, pakai 2PC saja" 2PC punya failure mode blocking (coordinator mati = lock tertahan). Di sistem yang harus available, saga + idempotency + kompensasi lebih realistis — dengan konsekuensi eventual consistency yang harus didesain ke UI.
"Cardinality tinggi otomatis bagus" UUIDv4 punya cardinality tinggi dan distribusi bagus, tapi locality-nya nol: semua query per-tenant jadi scatter-gather. Distribusi dan locality adalah trade-off, bukan dua hal yang bisa dimenangkan bersamaan.

✅ Cek Pemahaman

  1. Sistem multi-tenant SaaS: 5.000 tenant, 1 tenant = 45% traffic, 1 tenant = 22%, sisanya kecil. Query dominan: WHERE tenant_id = ? AND created_at > ?. Usulkan shard key dan jelaskan bagaimana kamu menangani dua tenant raksasa itu tanpa membuat tenant kecil menderita.
  2. Kamu punya collection MongoDB dengan shard key {created_at: 1} dan write 8k/s. Jelaskan mengapa menambah shard tidak menaikkan throughput, dan sebutkan dua jalur perbaikan beserta biayanya masing-masing.
  3. Kapan keputusan untuk tidak shard adalah keputusan arsitektur yang lebih baik? Sebutkan tiga sinyal konkret yang membuatmu memilih vertical scale + read replica.

🔑 Poin Kunci


➡️ Eksplorasi Lanjut