Skip to content

Consistent Hashing — Băm nhất quán cho hệ thống phân tán

Bạn có 4 cache server, dùng hash(key) % 4 để phân phối key. Hệ thống chạy mượt cho đến khi server thứ 3 chết. Lúc này hash(key) % 3 thay vì % 4 — gần như TOÀN BỘ key bị ánh xạ sang server khác. Cache miss rate nhảy lên gần 100%. Database phía sau bị storm request, latency tăng vọt, rồi cascade failure kéo sập cả hệ thống. Đây không phải kịch bản giả định — đây là sự cố thực tế từng xảy ra tại nhiều công ty.

Consistent Hashing giải quyết bài toán này một cách thanh lịch. Khi thêm hoặc bớt một node, chỉ khoảng 1/n keys cần di chuyển (thay vì gần toàn bộ). Kỹ thuật này là nền tảng của Amazon DynamoDB, Apache Cassandra, Memcached, Akamai CDN, và hầu hết mọi hệ thống phân tán hiện đại.

Bức tranh tư duy

Hình dung một bàn tròn (mặt đồng hồ) với 360 vị trí (0° đến 359°). Mỗi server được hash vào một vị trí trên bàn tròn. Mỗi key cũng được hash vào một vị trí. Key thuộc về server gần nhất theo chiều kim đồng hồ.

Hash Ring (0 → 2³² - 1, biểu diễn đơn giản):

        0 / 360°
          |
    S4 ·  |  · Key_A
          |
  270° ----+---- 90°
          |
  Key_C · |  · S1
          |
        180°
      · S2  · Key_B
            · S3

Key_A → theo chiều kim đồng hồ → gặp S1 → thuộc S1
Key_B → theo chiều kim đồng hồ → gặp S3 → thuộc S3
Key_C → theo chiều kim đồng hồ → gặp S4 → thuộc S4

Khi S3 chết: Key_B di chuyển sang S4 (server tiếp theo trên ring).
Chỉ keys giữa S2 và S3 bị ảnh hưởng — phần còn lại không đổi.

Khi thêm hoặc bớt server, chỉ keys nằm giữa server bị ảnh hưởng và server liền kề cần di chuyển. Phần lớn keys giữ nguyên vị trí. Đây là sức mạnh cốt lõi của consistent hashing.

Cốt lõi kỹ thuật

Thảm họa của Modular Hashing

Với n server, server = hash(key) % n. Khi n thay đổi (thêm/bớt server), hầu hết keys bị remap.

Sự kiệnModular HashingConsistent Hashing
1 server chết (4 → 3)~75% keys di chuyển~25% keys di chuyển
Thêm 1 server (4 → 5)~80% keys di chuyển~20% keys di chuyển
Scale 10 → 100 servers~90% keys di chuyển~10% keys di chuyển

Hash Ring — Thuật toán cơ bản

Ý tưởng: coi không gian hash [0, 2³²) như một vòng tròn. Mỗi server và mỗi key được hash vào một điểm trên vòng. Key thuộc về server gần nhất theo chiều kim đồng hồ.

cpp
#include <map>
#include <string>
#include <functional>

class ConsistentHashRing {
    std::map<uint32_t, std::string> ring_;  // hash → server
    std::hash<std::string> hasher_;

    uint32_t hashKey(const std::string& key) {
        return static_cast<uint32_t>(hasher_(key));
    }

public:
    void addServer(const std::string& server) {
        ring_[hashKey(server)] = server;
    }

    void removeServer(const std::string& server) {
        ring_.erase(hashKey(server));
    }

    std::string getServer(const std::string& key) {
        if (ring_.empty()) return "";
        uint32_t h = hashKey(key);
        // Tìm server đầu tiên có hash >= h (clockwise)
        auto it = ring_.lower_bound(h);
        if (it == ring_.end())
            it = ring_.begin();  // wrap around
        return it->second;
    }
};
python
import hashlib
from bisect import bisect_right

class ConsistentHashRing:
    def __init__(self) -> None:
        self.ring: dict[int, str] = {}
        self.sorted_hashes: list[int] = []

    def _hash(self, key: str) -> int:
        digest = hashlib.md5(key.encode()).hexdigest()
        return int(digest[:8], 16)

    def add_server(self, server: str) -> None:
        h = self._hash(server)
        self.ring[h] = server
        self.sorted_hashes = sorted(self.ring.keys())

    def remove_server(self, server: str) -> None:
        h = self._hash(server)
        if h in self.ring:
            del self.ring[h]
            self.sorted_hashes = sorted(self.ring.keys())

    def get_server(self, key: str) -> str:
        if not self.ring:
            return ""
        h = self._hash(key)
        idx = bisect_right(self.sorted_hashes, h)
        if idx == len(self.sorted_hashes):
            idx = 0  # wrap around
        return self.ring[self.sorted_hashes[idx]]
java
import java.util.TreeMap;
import java.security.MessageDigest;

public class ConsistentHashRing {
    private final TreeMap<Long, String> ring = new TreeMap<>();

    private long hash(String key) {
        try {
            MessageDigest md = MessageDigest.getInstance("MD5");
            byte[] digest = md.digest(key.getBytes());
            return ((long)(digest[0] & 0xFF) << 24)
                 | ((long)(digest[1] & 0xFF) << 16)
                 | ((long)(digest[2] & 0xFF) << 8)
                 | (digest[3] & 0xFF);
        } catch (Exception e) {
            return key.hashCode() & 0xFFFFFFFFL;
        }
    }

    public void addServer(String server) {
        ring.put(hash(server), server);
    }

    public void removeServer(String server) {
        ring.remove(hash(server));
    }

    public String getServer(String key) {
        if (ring.isEmpty()) return null;
        long h = hash(key);
        var entry = ring.ceilingEntry(h);
        if (entry == null) entry = ring.firstEntry();
        return entry.getValue();
    }
}
go
import (
    "crypto/md5"
    "encoding/binary"
    "sort"
)

type ConsistentHashRing struct {
    ring       map[uint32]string
    sortedKeys []uint32
}

func NewRing() *ConsistentHashRing {
    return &ConsistentHashRing{ring: make(map[uint32]string)}
}

func (r *ConsistentHashRing) hash(key string) uint32 {
    h := md5.Sum([]byte(key))
    return binary.BigEndian.Uint32(h[:4])
}

func (r *ConsistentHashRing) AddServer(server string) {
    h := r.hash(server)
    r.ring[h] = server
    r.sortedKeys = append(r.sortedKeys, h)
    sort.Slice(r.sortedKeys, func(i, j int) bool {
        return r.sortedKeys[i] < r.sortedKeys[j]
    })
}

func (r *ConsistentHashRing) GetServer(key string) string {
    if len(r.ring) == 0 {
        return ""
    }
    h := r.hash(key)
    idx := sort.Search(len(r.sortedKeys), func(i int) bool {
        return r.sortedKeys[i] >= h
    })
    if idx == len(r.sortedKeys) {
        idx = 0
    }
    return r.ring[r.sortedKeys[idx]]
}

Virtual Nodes — Giải quyết phân bố không đều

Vấn đề: với ít server, hash có thể phân bố không đều trên ring — một server nhận 60% keys, server khác chỉ 10%. Virtual nodes giải quyết bằng cách tạo nhiều "bản sao ảo" cho mỗi server trên ring.

cpp
class VirtualNodeRing {
    std::map<uint32_t, std::string> ring_;
    int replicas_;
    std::hash<std::string> hasher_;

    uint32_t hashKey(const std::string& key) {
        return static_cast<uint32_t>(hasher_(key));
    }

public:
    explicit VirtualNodeRing(int replicas = 150) : replicas_(replicas) {}

    void addServer(const std::string& server) {
        for (int i = 0; i < replicas_; i++) {
            std::string vnode = server + "#" + std::to_string(i);
            ring_[hashKey(vnode)] = server;
        }
    }

    void removeServer(const std::string& server) {
        for (int i = 0; i < replicas_; i++) {
            std::string vnode = server + "#" + std::to_string(i);
            ring_.erase(hashKey(vnode));
        }
    }

    std::string getServer(const std::string& key) {
        if (ring_.empty()) return "";
        auto it = ring_.lower_bound(hashKey(key));
        if (it == ring_.end()) it = ring_.begin();
        return it->second;
    }
};
python
class VirtualNodeRing:
    def __init__(self, replicas: int = 150) -> None:
        self.replicas = replicas
        self.ring: dict[int, str] = {}
        self.sorted_hashes: list[int] = []

    def _hash(self, key: str) -> int:
        digest = hashlib.md5(key.encode()).hexdigest()
        return int(digest[:8], 16)

    def add_server(self, server: str) -> None:
        for i in range(self.replicas):
            vnode_key = f"{server}#{i}"
            h = self._hash(vnode_key)
            self.ring[h] = server
        self.sorted_hashes = sorted(self.ring.keys())

    def remove_server(self, server: str) -> None:
        for i in range(self.replicas):
            vnode_key = f"{server}#{i}"
            h = self._hash(vnode_key)
            self.ring.pop(h, None)
        self.sorted_hashes = sorted(self.ring.keys())

    def get_server(self, key: str) -> str:
        if not self.ring:
            return ""
        h = self._hash(key)
        idx = bisect_right(self.sorted_hashes, h)
        if idx == len(self.sorted_hashes):
            idx = 0
        return self.ring[self.sorted_hashes[idx]]
java
public class VirtualNodeRing {
    private final TreeMap<Long, String> ring = new TreeMap<>();
    private final int replicas;

    public VirtualNodeRing(int replicas) {
        this.replicas = replicas;
    }

    public void addServer(String server) {
        for (int i = 0; i < replicas; i++) {
            long h = hash(server + "#" + i);
            ring.put(h, server);
        }
    }

    public void removeServer(String server) {
        for (int i = 0; i < replicas; i++) {
            ring.remove(hash(server + "#" + i));
        }
    }

    public String getServer(String key) {
        if (ring.isEmpty()) return null;
        var entry = ring.ceilingEntry(hash(key));
        if (entry == null) entry = ring.firstEntry();
        return entry.getValue();
    }

    private long hash(String key) {
        // Sử dụng hàm hash từ ví dụ trước
        return key.hashCode() & 0xFFFFFFFFL;
    }
}
go
type VirtualNodeRing struct {
    ring       map[uint32]string
    sortedKeys []uint32
    replicas   int
}

func NewVirtualNodeRing(replicas int) *VirtualNodeRing {
    return &VirtualNodeRing{
        ring:     make(map[uint32]string),
        replicas: replicas,
    }
}

func (r *VirtualNodeRing) hash(key string) uint32 {
    h := md5.Sum([]byte(key))
    return binary.BigEndian.Uint32(h[:4])
}

func (r *VirtualNodeRing) AddServer(server string) {
    for i := 0; i < r.replicas; i++ {
        vnode := fmt.Sprintf("%s#%d", server, i)
        h := r.hash(vnode)
        r.ring[h] = server
        r.sortedKeys = append(r.sortedKeys, h)
    }
    sort.Slice(r.sortedKeys, func(i, j int) bool {
        return r.sortedKeys[i] < r.sortedKeys[j]
    })
}

func (r *VirtualNodeRing) GetServer(key string) string {
    if len(r.ring) == 0 {
        return ""
    }
    h := r.hash(key)
    idx := sort.Search(len(r.sortedKeys), func(i int) bool {
        return r.sortedKeys[i] >= h
    })
    if idx == len(r.sortedKeys) {
        idx = 0
    }
    return r.ring[r.sortedKeys[idx]]
}

Jump Consistent Hashing

Google giới thiệu thuật toán Jump Consistent Hash (2014) — không cần ring, chỉ cần 1 hàm toán học. Cho key và số bucket, trả về bucket index. Đặc biệt hiệu quả khi chỉ thêm/bớt bucket ở cuối (scale up tuần tự).

cpp
// Google's Jump Consistent Hash — O(log n), no memory overhead
int32_t jumpConsistentHash(uint64_t key, int32_t numBuckets) {
    int64_t b = -1, j = 0;
    while (j < numBuckets) {
        b = j;
        key = key * 2862933555777941757ULL + 1;
        j = (int64_t)((b + 1) *
            ((double)(1LL << 31) / ((double)((key >> 33) + 1))));
    }
    return (int32_t)b;
}
python
def jump_consistent_hash(key: int, num_buckets: int) -> int:
    b, j = -1, 0
    while j < num_buckets:
        b = j
        key = ((key * 2862933555777941757) + 1) & ((1 << 64) - 1)
        j = int((b + 1) * ((1 << 31) / ((key >> 33) + 1)))
    return b
java
public static int jumpConsistentHash(long key, int numBuckets) {
    long b = -1, j = 0;
    while (j < numBuckets) {
        b = j;
        key = key * 2862933555777941757L + 1;
        j = (long) ((b + 1) *
            ((double)(1L << 31) / ((double)((key >>> 33) + 1))));
    }
    return (int) b;
}
go
func jumpConsistentHash(key uint64, numBuckets int32) int32 {
    var b, j int64 = -1, 0
    for j < int64(numBuckets) {
        b = j
        key = key*2862933555777941757 + 1
        j = int64(float64(b+1) *
            (float64(int64(1)<<31) / float64((key>>33)+1)))
    }
    return int32(b)
}

Jump Consistent Hash: O(log n) time, O(1) memory, phân bố gần như hoàn hảo. Hạn chế: chỉ hoạt động tốt khi thêm/bớt bucket ở cuối — không linh hoạt như ring cho việc xóa server tùy ý.

Thực chiến

Tình huống: Distributed Cache Layer

Bối cảnh: Hệ thống e-commerce với 5 cache server (Memcached). Traffic tăng, cần thêm 2 server. Với modular hashing, ~71% cache bị invalidate khi scale 5→7 server.

Mục tiêu: Scale mà chỉ ~29% keys cần migrate (≈ 2/7).

python
class DistributedCache:
    def __init__(self, servers: list[str], replicas: int = 150) -> None:
        self.ring = VirtualNodeRing(replicas)
        self.caches: dict[str, dict] = {}
        for server in servers:
            self.add_server(server)

    def add_server(self, server: str) -> None:
        self.ring.add_server(server)
        self.caches[server] = {}

    def remove_server(self, server: str) -> list[tuple[str, object]]:
        # Thu thập keys cần migrate
        orphaned = list(self.caches.get(server, {}).items())
        self.ring.remove_server(server)
        del self.caches[server]
        # Re-route orphaned keys
        for key, value in orphaned:
            new_server = self.ring.get_server(key)
            if new_server:
                self.caches[new_server][key] = value
        return orphaned

    def get(self, key: str) -> object | None:
        server = self.ring.get_server(key)
        if not server:
            return None
        return self.caches[server].get(key)

    def put(self, key: str, value: object) -> None:
        server = self.ring.get_server(key)
        if server:
            self.caches[server][key] = value
cpp
#include <unordered_map>
#include <string>

class DistributedCache {
    VirtualNodeRing ring_;
    std::unordered_map<std::string,
        std::unordered_map<std::string, std::string>> caches_;

public:
    explicit DistributedCache(int replicas = 150)
        : ring_(replicas) {}

    void addServer(const std::string& server) {
        ring_.addServer(server);
        caches_[server] = {};
    }

    void put(const std::string& key, const std::string& value) {
        auto server = ring_.getServer(key);
        if (!server.empty())
            caches_[server][key] = value;
    }

    std::string get(const std::string& key) {
        auto server = ring_.getServer(key);
        if (server.empty()) return "";
        auto it = caches_[server].find(key);
        return it != caches_[server].end() ? it->second : "";
    }
};
java
import java.util.*;

public class DistributedCache {
    private final VirtualNodeRing ring;
    private final Map<String, Map<String, Object>> caches = new HashMap<>();

    public DistributedCache(List<String> servers, int replicas) {
        this.ring = new VirtualNodeRing(replicas);
        for (String server : servers) addServer(server);
    }

    public void addServer(String server) {
        ring.addServer(server);
        caches.put(server, new HashMap<>());
    }

    public void put(String key, Object value) {
        String server = ring.getServer(key);
        if (server != null) caches.get(server).put(key, value);
    }

    public Object get(String key) {
        String server = ring.getServer(key);
        if (server == null) return null;
        return caches.getOrDefault(server, Map.of()).get(key);
    }
}
go
type DistributedCache struct {
    ring   *VirtualNodeRing
    caches map[string]map[string]interface{}
}

func NewDistributedCache(servers []string, replicas int) *DistributedCache {
    dc := &DistributedCache{
        ring:   NewVirtualNodeRing(replicas),
        caches: make(map[string]map[string]interface{}),
    }
    for _, s := range servers {
        dc.AddServer(s)
    }
    return dc
}

func (dc *DistributedCache) AddServer(server string) {
    dc.ring.AddServer(server)
    dc.caches[server] = make(map[string]interface{})
}

func (dc *DistributedCache) Put(key string, value interface{}) {
    server := dc.ring.GetServer(key)
    if server != "" {
        dc.caches[server][key] = value
    }
}

func (dc *DistributedCache) Get(key string) interface{} {
    server := dc.ring.GetServer(key)
    if server == "" { return nil }
    return dc.caches[server][key]
}

Phân tích:

  • Scale 5→7 server: chỉ ~2/7 ≈ 29% keys migrate thay vì ~71% với modular hashing.
  • Virtual nodes (150 per server) đảm bảo phân bố đều — standard deviation < 5%.
  • Remove server: orphaned keys tự động re-route sang server tiếp theo trên ring.

Sai lầm điển hình

Sai lầm 1: Không dùng virtual nodes

Vấn đề: Chỉ hash mỗi server một lần lên ring. Với 4 server, phân bố có thể lệch nặng: server A nhận 40% keys, server D chỉ 10%.

Tại sao sai: Hash function không đảm bảo phân bố đều với ít điểm. Virtual nodes (100-200 bản sao mỗi server) tạo đủ nhiều điểm trên ring để thống kê tiến về đều.

Sai lầm 2: Dùng hash function yếu

Vấn đề: Dùng hashCode() (Java) hoặc hash() (Python) — các hàm này thiết kế cho hash table, không đảm bảo phân bố đều trên ring.

Tại sao sai: Clustering effect — nhiều keys dồn vào một vùng nhỏ trên ring. Dùng MD5, SHA-1, hoặc xxHash cho phân bố tốt hơn.

python
# SAI: hash() của Python không đều trên ring
h = hash(key) % (2**32)

# ĐÚNG: dùng MD5 hoặc xxHash
import hashlib
h = int(hashlib.md5(key.encode()).hexdigest()[:8], 16)

Sai lầm 3: Quên xử lý data migration khi add/remove server

Vấn đề: Thêm server vào ring nhưng không migrate keys từ server cũ. Kết quả: cache miss cho keys đã bị remap, nhưng data vẫn nằm ở server cũ.

Tại sao sai: Consistent hashing chỉ quyết định key THUỘC server nào — không tự động di chuyển data. Cần migration logic riêng hoặc chấp nhận cache warm-up period.

Sai lầm 4: Quá nhiều virtual nodes

Vấn đề: Dùng 10.000 virtual nodes per server → ring có hàng triệu entries → lookup chậm, bộ nhớ lớn.

Tại sao sai: Diminishing returns. 150-200 virtual nodes đã cho phân bố đều với standard deviation < 5%. Tăng lên 10.000 cải thiện không đáng kể nhưng tăng memory và lookup time.

Under the Hood

Hiệu năng

Thao tácHash Ring (basic)Hash Ring + Virtual NodesJump Consistent Hash
LookupO(log n)O(log(n × v))O(log n)
Add serverO(v log(n × v))O(v log(n × v))O(1)
Remove serverO(v log(n × v))O(v log(n × v))Không hỗ trợ tốt
MemoryO(n)O(n × v)O(1)
Key redistributionK/n expectedK/n expectedK/n expected

Trong đó: n = số server, v = virtual nodes per server, K = tổng số keys.

Nội bộ triển khai

Phân tích toán học key redistribution: Khi thêm 1 server vào ring có n server, server mới nhận một đoạn arc trên ring. Expected size = 1/(n+1) tổng ring. Số keys di chuyển expected = K/(n+1). Đây là tối ưu — không thể làm tốt hơn vì mỗi server cần nhận 1/(n+1) tổng keys.

Virtual node count tuning: Với v virtual nodes per server, standard deviation của load distribution ≈ ε/sqrt(v) với ε phụ thuộc hash function. Thực nghiệm: v = 100 cho std dev ~10%, v = 200 cho ~5%, v = 500 cho ~3%. Trade-off: memory O(n × v) và lookup time O(log(n × v)). Với n = 100 server, v = 200: ring có 20.000 entries — binary search vẫn chỉ ~15 bước.

Jump Consistent Hash internals: Thuật toán sử dụng linear congruential generator để tạo chuỗi pseudo-random. Key insight: xác suất bucket j là kết quả cuối cùng = 1/(j+1). Khi thêm bucket n+1, chỉ keys có kết quả đúng bằng n mới có thể chuyển — đảm bảo 1/(n+1) keys di chuyển.

Trade-offs

Tiêu chíConsistent HashingModular HashingRendezvous Hashing
Key redistributionK/n (tối ưu)~K (gần toàn bộ)K/n (tối ưu)
Lookup timeO(log n)O(1)O(n)
MemoryO(n × v)O(1)O(n)
ImplementationTrung bìnhĐơn giảnĐơn giản
FlexibilityCao (add/remove any)ThấpCao

Consistent hashing thắng ở key redistribution và flexibility. Modular hashing chỉ phù hợp khi cluster size cố định. Rendezvous hashing (highest random weight) đơn giản hơn nhưng lookup O(n) — không scale với cluster lớn.

Checklist ghi nhớ

✅ Checklist triển khai

Thiết kế

  • [ ] Modular hashing (% n) gây cache storm khi thêm/bớt server — luôn dùng consistent hashing
  • [ ] Luôn dùng virtual nodes (100-200 per server) — không bao giờ chỉ 1 point per server
  • [ ] Dùng hash function chất lượng (MD5, xxHash) — tránh hashCode() cho ring

Vận hành

  • [ ] Thêm/bớt server cần migration logic — consistent hashing không tự di chuyển data
  • [ ] Monitor load distribution — virtual nodes giảm nhưng không triệt tiêu imbalance
  • [ ] Cân nhắc replication factor: key lưu ở k server liên tiếp trên ring cho fault tolerance

Lựa chọn thuật toán

  • [ ] General purpose, add/remove linh hoạt → Hash Ring + Virtual Nodes
  • [ ] Scale up tuần tự, không cần remove → Jump Consistent Hash
  • [ ] Cluster size cố định → Modular hashing đủ dùng

Bài tập luyện tập

Bài 1: Cài đặt Hash Ring cơ bản — Intermediate

Đề bài: Cài đặt consistent hash ring hỗ trợ add_server, remove_server, get_server. Kiểm chứng: thêm 1 server vào ring 4 server, đo tỉ lệ keys bị remap (phải xấp xỉ 20%).

🧠 Quiz

Câu hỏi: Một hash ring có 5 server (không có virtual nodes). Khi remove 1 server, expected percentage keys cần migrate là?

  • [ ] A. 100%
  • [ ] B. 50%
  • [x] C. 20%
  • [ ] D. 5% Giải thích: Chỉ keys thuộc server bị remove mới di chuyển sang server tiếp theo trên ring. Expected: 1/5 = 20%. Đáp án A (100%) là modular hashing. Đáp án B (50%) không có cơ sở toán học.
💡 Gợi ý
  • Dùng sorted array + binary search (hoặc TreeMap) cho ring.
  • Test: hash 10.000 random keys, ghi nhận server cho mỗi key. Thêm 1 server, hash lại, đếm keys bị remap.
✅ Lời giải
python
import hashlib, random

class SimpleRing:
    def __init__(self):
        self.ring = {}
        self.sorted_hashes = []

    def _hash(self, key):
        return int(hashlib.md5(key.encode()).hexdigest()[:8], 16)

    def add(self, server):
        h = self._hash(server)
        self.ring[h] = server
        self.sorted_hashes = sorted(self.ring.keys())

    def get(self, key):
        if not self.ring: return ""
        from bisect import bisect_right
        h = self._hash(key)
        idx = bisect_right(self.sorted_hashes, h) % len(self.sorted_hashes)
        return self.ring[self.sorted_hashes[idx]]

# Test migration rate
ring = SimpleRing()
for s in ["S1", "S2", "S3", "S4"]:
    ring.add(s)

keys = [f"key_{i}" for i in range(10000)]
before = {k: ring.get(k) for k in keys}

ring.add("S5")
after = {k: ring.get(k) for k in keys}

moved = sum(1 for k in keys if before[k] != after[k])
print(f"Keys moved: {moved}/10000 = {moved/100:.1f}%")
# Expected: ~20% (khoang 1800-2200)

Phân tích: Kết quả thực tế dao động quanh 20% — đúng với lý thuyết 1/(n+1) = 1/5.

Liên kết học tiếp

Từ khóa glossary: Consistent Hashing, Hash Ring, Virtual Nodes, Jump Consistent Hash, Distributed Cache, Load Balancing, Sharding

Tìm kiếm liên quan: băm nhất quán, phân tán dữ liệu, hash ring, virtual node, distributed cache, database sharding