Consistent Hashing System Design (original) (raw)

Last Updated : 4 May, 2026

Consistent hashing is a technique used in distributed systems and load balancing to distribute data or requests across multiple servers efficiently. It reduces the amount of re-mapping (rehashing) needed when servers are added or removed, improving scalability and stability.

**Example: In a caching system (like Redis cluster), if a new server is added, only some keys move to the new server instead of reassigning all keys, which improves performance and reduces downtime.

devops_27

Consistent Hashing

**Note: In consistent hashing, a request is mapped to the first server encountered in the clockwise direction on the hash ring after hashing the request key.

How Requests Are Assigned to Servers

In consistent hashing, both servers and requests are placed on a virtual ring using a hash function. For any incoming request, the system moves clockwise on the ring and assigns the request to the first server it encounters. This ensures a balanced and predictable distribution of requests.

**Example: Imagine a hash ring with three servers placed at different positions: Node 1, Node 2, and Node 5. If a request is hashed to a position between Node 1 and Node 2, it will be handled by Node 2 (the next server in the clockwise direction). If Node 2 goes down, the same request will now be handled by Node 5, minimizing redistribution of other requests.

devops_28

Mapping in the Hashing

Issues with Traditional Hashing Methods

Here are some key issues with traditional hashing methods explained in easy language:

Uses

Consistent hashing is used to efficiently distribute data across servers with minimal re-mapping during changes.

Phases/Working of Consistent Hashing

The following are the phases involved in the process of consistent hashing:

**Example: Let's say we have 5 nodes in the ring and say node 3 fails, then the range of the next server node widens and any request coming in all of this range, goes to the new server node. This shows that due to use of consistent hashing only a small portion of keys are affected.

devops_29

Node failure

Virtual Nodes

To improve load distribution, each physical server is represented by multiple virtual nodes on the hash ring.

Implementation of Consistent Hashing algorithm

Consistent hashing distributes data across servers efficiently while minimizing data movement when nodes are added or removed.

**Implementation

C++ `

#include <bits/stdc++.h>

using namespace std;

class ConsistentHashRing { private: map<int, string> ring; set sorted_keys; int replicas;

int get_hash(const string& value) {
    hash<string> hash_function;
    return hash_function(value);
}

public: ConsistentHashRing(int replicas = 3) : replicas(replicas) {}

  // Function to add Node in the ring
void add_node(const string& node) {
    for (int i = 0; i < replicas; ++i) {
        int replica_key = get_hash(node + "_" + to_string(i));
        ring[replica_key] = node;
        sorted_keys.insert(replica_key);
    }
}

  // Function to remove Node from the ring
void remove_node(const string& node) {
    for (int i = 0; i < replicas; ++i) {
        int replica_key = get_hash(node + "_" + to_string(i));
        ring.erase(replica_key);
        sorted_keys.erase(replica_key);
    }
}

string get_node(const string& key) {
    if (ring.empty()) {
        return "";
    }

    int hash_value = get_hash(key);
    auto it = sorted_keys.lower_bound(hash_value);

    if (it == sorted_keys.end()) {
        // Wrap around to the beginning of the ring
        it = sorted_keys.begin();
    }

    return ring[*it];
}

};

int main() { ConsistentHashRing hash_ring;

// Add nodes to the ring
hash_ring.add_node("Node_A");
hash_ring.add_node("Node_B");
hash_ring.add_node("Node_C");

// Get the node for a key
string key = "first_key";
string node = hash_ring.get_node(key);

cout << "The key '" << key << "' is mapped to node: " << node << endl;

return 0;

}

Java

import java.util.; import java.util.stream.;

class ConsistentHashRing { private Map<Integer, String> ring = new HashMap<>(); private TreeSet sortedKeys = new TreeSet<>(); private int replicas;

private int getHash(String value) {
    return value.hashCode();
}

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

public void addNode(String node) {
    for (int i = 0; i < replicas; ++i) {
        int replicaKey = getHash(node + "_" + i);
        ring.put(replicaKey, node);
        sortedKeys.add(replicaKey);
    }
}

public void removeNode(String node) {
    for (int i = 0; i < replicas; ++i) {
        int replicaKey = getHash(node + "_" + i);
        ring.remove(replicaKey);
        sortedKeys.remove(replicaKey);
    }
}

public String getNode(String key) {
    if (ring.isEmpty()) {
        return "";
    }

    int hashValue = getHash(key);
    Integer it = sortedKeys.ceiling(hashValue);

    if (it == null) {
        it = sortedKeys.first();
    }

    return ring.get(it);
}

}

public class Main { public static void main(String[] args) { ConsistentHashRing hashRing = new ConsistentHashRing(3);

    hashRing.addNode("Node_A");
    hashRing.addNode("Node_B");
    hashRing.addNode("Node_C");

    String key = "first_key";
    String node = hashRing.getNode(key);

    System.out.println("The key '" + key + "' is mapped to node: " + node);
}

}

Python

from hashlib import md5 from bisect import bisect

class ConsistentHashRing: def init(self, replicas): self.ring = {} self.sorted_keys = [] self.replicas = replicas

def get_hash(self, value):
    return int(md5(value.encode()).hexdigest(), 16)

def add_node(self, node):
    for i in range(self.replicas):
        replica_key = self.get_hash(node + "_" + str(i))
        self.ring[replica_key] = node
        bisect(self.sorted_keys, replica_key)

def remove_node(self, node):
    for i in range(self.replicas):
        replica_key = self.get_hash(node + "_" + str(i))
        del self.ring[replica_key]
        self.sorted_keys.remove(replica_key)

def get_node(self, key):
    if not self.ring:
        return ""

    hash_value = self.get_hash(key)
    idx = bisect(self.sorted_keys, hash_value) % len(self.sorted_keys)
    return self.ring[self.sorted_keys[idx]]

if name == "main": hash_ring = ConsistentHashRing(3)

hash_ring.add_node("Node_A")
hash_ring.add_node("Node_B")
hash_ring.add_node("Node_C")

key = "first_key"
node = hash_ring.get_node(key)

print(f'The key '{key}' is mapped to node: {node}')

`

Output

The key 'first_key' is mapped to node: Node_C

**Note: This example uses a simple hash function and a binary search to find the position on the ring.

Advantages

Consistent hashing provides efficient data distribution and improves system scalability and reliability in distributed environments.

Disadvantages

Despite its benefits, consistent hashing introduces complexity and may have some performance overhead in certain scenarios.