← semua materi
05:00 Arsitektur · 11 Oct 2026 · 22:10

🔬 BACOT — Sesi Deep Learning: Consistent Hashing

📚 Topik

Consistent Hashing — teknik distribusi data/kunci ke sekumpulan node yang tahan terhadap perubahan jumlah node, tanpa harus me-remap hampir seluruh kunci.

Kategori: Distributed Systems · Level: Intermediate–Advanced (diff 3)


🎯 Tujuan Belajar

Setelah sesi ini kamu harus bisa: 1. Menjelaskan kenapa hashing modulo n naif gagal di sistem terdistribusi. 2. Menjelaskan mekanisme hash ring dan kenapa ia memetakan ulang hanya ~K/n kunci saat satu node berubah. 3. Memahami virtual nodes (vnodes) sebagai solusi untuk masalah distribusi tidak merata. 4. Menyebut implementasi nyata dan trade-off-nya.


🧠 Penjelasan Inti

Masalah yang diselesaikan

Bayangkan kamu punya cache atau database terdistribusi dengan n server. Cara termudah menaruh kunci k ke server:

server_index = hash(k) mod n

Ini bekerja sampai n berubah. Kalau satu server mati atau ditambah, n → n±1, dan hampir semua kunci berubah pemetaan:

pindah  = (n_lama / n_baru)  ≈ 100%   # untuk n yang berubah sedikit

Contoh: n=10 → n=11. Hanya hash(k) mod 10 == hash(k) mod 11 yang tidak pindah = 1/11 ≈ 9%. Artinya ~91% kunci remap. Untuk cache, ini = cache miss massal (thundering herd) > database overload > potentially cascading failure. Ini yang disebut rehashing problem / redistribution storm.

Consistent hashing (Karger et al., 1997 — paper yang mempopulerkannya, dipakai di Akamai CDN) memecahkan ini: saat n berubah, hanya sekitar K/n kunci (≈ 1/n) yang pindah — yaitu kunci yang memang "milik" node yang berubah.

Ide inti

  1. Bayangkan ruang hash sebagai lingkaran/ring (biasanya [0, 2^32) atau [0, 2^128)).
  2. Setiap node di-hash ke satu titik di ring → node menempati posisi.
  3. Setiap kunci di-hash ke titik di ring.
  4. Kunci dimiliki oleh node pertama yang ditemui searah jarum jam dari posisi kunci.
        hash(k1)
           |
   [N1]----o----------[N2]
     \                 \
      \                 o hash(k2)
       \               /
        [N3]---------'

k1 → N2, k2 → N3, dst. Aturannya sederhana: clockwise successor.


⚙️ Cara Kerja Internal

1. Lookup

pos_key = hash(key)                      # titik di ring
node    = cari node terkecil >= pos_key  # clockwise successor
# kalau tidak ada (pos_key > semua node), wrap ke node pertama

Implementasi efisien: simpan posisi node di array terurut → binary search O(log n). Alternatif: balanced BST.

2. Kenapa hanya ~1/n kunci pindah saat node berubah

Bukti intuitif: node N_new menyisip ke ring di suatu titik. Ia hanya mengambil alih arc (segmen busur) dari predecessor-nya sampai posisinya sendiri — yaitu 1/n dari total ring rata-rata. Kunci di arc itu yang berpindah ke N_new; sisanya tak tersentuh.

Saat node N_off keluar: arc-nya jatuh ke successor-nya. Hanya kunci milik N_off yang remap. Jadi K/n, bukan K.

Ini bukan "lebih cepat dihitung" — biaya lookup sama-sama sekali hash. Yang berubah adalah stabilitas pemetaan: jumlah kunci yang bergerak proporsional ke perubahan, bukan ke total.

3. Masalah distribusi tidak merata

Dengan node acak di ring, arc-nya tidak sama besar. Ini masalah klasik balls-and-bins: dengan n node acak, rasio beban terberat/tertinggi bisa jauh dari rata. Bahkan dengan banyak node, deviasi bisa ~log n faktor. Node "sial" yang duduk di arc besar akan kebanjiran.

4. Solusi: Virtual Nodes (vnodes)

Setiap node fisik di-hash ke banyak titik di ring (mis. 100–256 vnodes per node). Node fisik memiliki banyak "placeholder" yang tersebar acak.

Efeknya: - Beban makin merata (law of large numbers antar-vnode). - Saat node masuk/keluar, kuncinya terdistribusi ke banyak node lain, bukan menumpuk di satu successor (mengurangi hot spot). - Node dengan kapasitas beda bisa diberi bobot: node besar → lebih banyak vnode. - Cost: metadata lebih besar (n × vnodes entri), lookup tetap O(log(n·v)).

Ini standar di industri — Dynamo (Amazon), Cassandra, Riak semuanya pakai vnodes.

5. Replikasi & failure (kenapa ini bukan cuma soal distribusi)

Di sistem nyata, tiap kunci direplikasi ke R node berikutnya searah jarum jam (N1→N2→N3). Kalau satu node mati, replica-nya masih ada. Ini yang membuat Dynamo tahan node death tanpa downtime data. Consistent hashing + replication preference list = fondasi storage terdistribusi.

6. Termination & kenapa tanpa koordinasi

Karena ring bisa dihitung deterministik dari daftar node, setiap client/node bisa menghitung pemetaan secara lokal — tak perlu registry terpusat. Ideal untuk sistem P2P dan sistem yang toleran partisi.


💡 Contoh Konkret

Cache layer: 3 node, K=10⁶ kunci, hash range 0–999 agar jelas.

Posisi (acak): N1=150, N2=520, N3=880.

Kunci hash Owner (clockwise)
a 100 N1 (150)
b 149 N1 (150)
c 151 N2 (520)
d 900 N1 (150, wrap)
e 519 N2 (520)

Sekarang tambah N4=300: - Arc N1→sebelumnya: N1 rentang (150, 520). N4 di 300 → ambil alih kunci dengan hash ∈ (150, 300]. - Hanya kunci di window selebar 150 (≈15% ring) yang pindah. Realita dengan n node acak → rata-rata ~1/n pindah. - Bandingkan mod 3→4: ~75% kunci remap. Bedanya dramatis.

Dengan vnodes (10 per node), N1 punya N1a..N1j tersebar. Hapus N1 → beban terbagi ke banyak node, bukan satu tetangga.

Analogi: Meja bundar dengan kursi. Tamu (kunci) duduk di kursi kosong terdekat searah jarum jam dari mereka. Kursi (node) dicopot/ditambah → hanya tetangga terdekat yang terganggu, bukan semua orang harus ganti tempat. Virtual nodes = setiap orang duduk di banyak tempat sekaligus dengan nama berbeda, jadi semua kursi lebih terisi merata.


⚠️ Miskonsepsi Umum

  1. "Consistent hashing = hashing yang konsisten (deterministik)." Salah. Semua hash deterministik. "Consistent" di sini spesifik: kestabilan pemetaan saat anggota berubah. Bukan tentang determinisme.

  2. "Node di-hash sekali, itu cukup." Dalam praktik hampir selalu tidak — tanpa vnodes, distribusi buruk. Implementasi produksi wajib pakai vnodes.

  3. "Consistent hashing butuh koordinasi/registry pusat." Tidak. Ia justru menghindari itu; semua pihak hitung lokal dari daftar node.

  4. "Hanya ~1/n kunci pindah, jadi beban juga imbalanced 1/n." Tidak benar tanpa vnodes. Yang pindah jumlahnya kecil, tapi siapa yang menerima bisa menumpuk (satu successor kewalahan). Vnodes memperbaiki ini.

  5. "Ini menggantikan replication." Tidak. Ini penempatan; durability tetap butuh replication/failure detection. Keduanya komplementer.

  6. "Hash ring = satu-satunya jawaban." Ada alternatif nyata: rendezvous hashing (HRW) — bahas di trade-off.


⚖️ Trade-off & Limitasi

Consistent hashing (hash ring + vnodes) - Pro: pemetaan stabil, scalable, tanpa koordinator, dukung node berbobot. - Kontra: kompleksitas metadata (n × v entri); perlu vnodes untuk balance; hotspot tetap mungkin kalau data skewed (mis. kunci populer).

Rendezvous hashing (Highest Random Weight / HRW) - Pro: sederhana (tak ada ring, tak ada vnode); balance lebih baik; minimal movement. - Kontra: O(n) per lookup (bandingkan semua node) vs O(log n) ring — masalah untuk n besar. Ada varian (Skeleton/anchored) untuk memperbaiki ini.

Jump consistent hash (Lamping & Veach, 2014, Google) - Pro: sangat ringan, O(log n), memori konstan, distribusi nyaris sempurna. - Kontra: hanya mendukung tambah/hapus di ujung (bukti bucket sequential); tidak bisa hapus node arbitrer. Cocok untuk sharding yang tumbuh monoton (contoh: memcached bucket add).

Belum tuntas / area sulit: - Hot keys — consistent hashing mendistribusi kunci, bukan beban. Satu kunci viral tetap = satu node. Butuh mekanisme lain (key splitting, local caching). - Data migration cost — jumlah kunci yang pindah stabil, tapi memindahkan data aktual (jaringan/disk) tetap mahal. Konsistensi selama migrasi (hint handoff di Dynamo) itu rumit. - Awareness topologi (rack/zone-aware placement) bukan bagian dari basic algorithm; butuh lapisan tambahan.


🛠️ Latihan Praktis

Implementasi ring minimal (~30 baris Python) dan uji klaim "hanya ~1/n pindah":

import hashlib, bisect

def h(x): return int(hashlib.md5(str(x).encode()).hexdigest(), 16)

class Ring:
    def __init__(self, nodes, vnodes=100):
        self.vnodes = vnodes
        self.ring = {}                      # pos -> node fisik
        self.sorted = []
        for n in nodes:
            self.add(n)
    def add(self, n):
        for i in range(self.vnodes):
            p = h(f"{n}#{i}")
            self.ring[p] = n
        self.sorted = sorted(self.ring)
    def get(self, key):
        p = h(key)
        i = bisect.bisect(self.sorted, p) % len(self.sorted)
        return self.ring[self.sorted[i]]

# self-check: pindah ~1/n
keys = [f"k{i}" for i in range(10000)]
r1 = Ring(["A","B","C"])
r2 = Ring(["A","B","C","D"])
moved = sum(r1.get(k) != r2.get(k) for k in keys)
assert 0.20 < moved/len(keys) < 0.30   # ~25% ≈ 1/4 dari 3->4

Coba juga: variasi vnodes (1 vs 200) dan ukur standar deviasi beban antar node. Tanpa vnodes (v=1) distribusi jelek; dengan v≥100 mendekati rata. Itu demonstrasi kenapa vnodes wajib.


✅ Cek Pemahaman

  1. Cache 100 node, 10⁸ kunci, pakai consistent hashing. Satu node mati. Kira-kira berapa kunci remap, dan ke node mana? Jawaban: ~1/100 dari kunci (kunci milik node itu), jatuh ke successor clockwise-nya (sebelum vnodes).

  2. Kenapa vnodes memperbaiki masalah di soal 1? Jawaban: vnodes menyebar kepemilikan node yang mati ke banyak node lain, mencegah satu successor kebanjiran.

  3. Kapan kamu tidak pilih consistent hashing? Jawaban: kalau node hanya tumbuh di ujung (→ jump consistent hash lebih ringan), atau n kecil & jarang berubah (→ mod n cukup), atau butuh balance maksimal tanpa vnode (→ rendezvous hashing).


🔑 Poin Kunci


➡️ Eksplorasi Lanjut

Replication strategies di sistem terdistribusi: quorum (R + W > N), hinted handoff, read repair, dan anti-entropy via Merkle trees — bagaimana Dynamo/Cassandra menjaga konsistensi data di atas consistent hashing. Ini jawaban natural untuk "setelah kunci ditempatkan, bagaimana durability & consistency-nya dijaga?"