一致性hash和hash有什么区别?和代码实现
一致性哈希(Consistent Hashing)和普通哈希(Hashing)的区别主要体现在数据分布、扩展性和容错性上。它们的核心思想都是将输入(通常是键)映射到某个值(通常是一个哈希值),但应用场景和实现细节有所不同。以下是两者的主要区别:
1. 普通哈希(Hashing)
普通哈希是将输入(比如一个键)通过哈希函数映射到固定范围内的一个值(比如数组的索引)。常见的哈希算法包括 MD5、SHA-1 等。
特点:
- 简单映射:普通哈希函数通常将输入映射到一个固定的哈希空间,比如数组或固定大小的存储单元。
- 均匀性:哈希函数的设计目标是将输入均匀地分布在哈希空间中,以避免哈希冲突。
- 固定结构:在分布式系统中,普通哈希通常将数据分配到有限数量的节点上(例如,固定数量的缓存或存储节点)。
缺点:
- 扩展性差:当增加或删除一个节点时,普通哈希会重新分配大量的键。例如,如果你有 4 个存储节点并且需要添加一个新的节点,那么大部分数据可能需要重新分配,这会导致大量数据迁移,性能下降。
- 不具备容错性:在分布式系统中,如果一个节点宕机,普通哈希并不会自动调整数据的分布,也不能平衡负载。
2. 一致性哈希(Consistent Hashing)
一致性哈希是一种改进的哈希算法,主要用于分布式系统,尤其是当节点数量可能动态变化时(如增加或删除节点)。它通过在哈希空间中建立一个虚拟环,来减小节点的变动对整个系统的影响。
特点:
- 哈希环:一致性哈希将哈希值映射到一个环形结构上。节点和数据都在这个环上,数据映射到离它最近的节点上。
- 扩展性好:在一致性哈希中,增加或删除节点时,只需要重新分配少量的数据。例如,新增一个节点后,只会将该节点附近的数据重新分配,而不会影响整个系统。
- 容错性强:一致性哈希支持容错。如果一个节点宕机,它的数据会自动映射到它周围的其他节点,减少数据丢失的风险。
- 虚拟节点:为了提高负载均衡性,一致性哈希通常使用虚拟节点,将一个物理节点映射到多个哈希位置。这有助于避免数据倾斜问题,即某些节点负载过高。
优点:
- 负载平衡:由于数据按需重新分配,扩展或缩减节点时,整个系统负载更平衡。
- 高容错:在节点故障时,数据可以被其他节点接管,减少宕机带来的影响。
- 可扩展性强:新增或删除节点时只会影响较少的键。
区别总结:
特性 | 普通哈希(Hashing) | 一致性哈希(Consistent Hashing) |
---|---|---|
数据分布 | 固定的哈希空间,数据均匀分布 | 哈希环上的分布,数据靠近其节点 |
扩展性 | 扩展或减少节点需要重新分配大量数据 | 扩展或减少节点只影响部分数据 |
容错性 | 宕机时没有自动恢复机制 | 宕机时数据可由其他节点接管 |
适用场景 | 固定数量的节点或存储单元 | 动态扩展的分布式系统,负载均衡 |
使用场景:
- 普通哈希:适用于静态系统,节点数量固定的情况下使用,如哈希表或某些缓存场景。
- 一致性哈希:广泛用于分布式系统中,特别是在缓存系统(如 Redis、Memcached)、分布式存储系统、分布式数据库等场景中,节点数量经常变动的情况下。
例子:
-
普通哈希:在缓存系统中,数据通过
hash(key) % n
的方式映射到n
个缓存节点之一。添加或删除一个节点需要重新计算所有数据的映射。 -
一致性哈希:在分布式缓存系统中,假设你有 4 个缓存节点,当添加第 5 个节点时,只有一部分数据会被重新映射到新节点上,而不是重新映射所有的数据。
一致性哈希在动态扩展和容错性方面有明显优势,是分布式系统中常用的一种哈希方式。
普通hash算法
package com.aixiao.business.controller; import java.util.HashMap; import java.util.Map; public class HashExample { public static void main(String[] args) { // 定义三个服务器节点 String[] servers = {"Server1", "Server2", "Server3"}; // 模拟请求键值 String[] keys = {"Key1", "Key2", "Key3", "Key4", "Key5"}; // 哈希键并将其分配到服务器上 for (String key : keys) { int serverIndex = key.hashCode() % servers.length; // 防止负数的索引值 serverIndex = (serverIndex < 0) ? -serverIndex : serverIndex; System.out.println("Key: " + key + " is assigned to " + servers[serverIndex]); } } }
一致性hash
一致性哈希的实现相对复杂一些,关键是要设计一个环状结构,并将节点和数据映射到环上。我们将物理节点映射到多个虚拟节点,以均衡负载,并确保在增加或移除节点时,只有一部分数据需要重新分配。
import java.nio.charset.StandardCharsets; import java.security.MessageDigest; import java.util.SortedMap; import java.util.TreeMap; public class ConsistentHashing { // 哈希环 private final SortedMap<Integer, String> hashRing = new TreeMap<>(); // 虚拟节点数量 private static final int VIRTUAL_NODES = 3; // 添加服务器节点到哈希环 public void addServer(String server) { for (int i = 0; i < VIRTUAL_NODES; i++) { int hash = hash(server + i); hashRing.put(hash, server); System.out.println("Added server: " + server + " with virtual node hash: " + hash); } } // 根据键查找所属的服务器节点 public String getServer(String key) { int hash = hash(key); SortedMap<Integer, String> tailMap = hashRing.tailMap(hash); // 如果没有比当前键哈希更大的节点,则取环的第一个节点 Integer nodeHash = tailMap.isEmpty() ? hashRing.firstKey() : tailMap.firstKey(); return hashRing.get(nodeHash); } // 使用MD5哈希函数将键值转换为哈希值 private int hash(String key) { try { MessageDigest md5 = MessageDigest.getInstance("MD5"); byte[] digest = md5.digest(key.getBytes(StandardCharsets.UTF_8)); return ((digest[3] & 0xFF) << 24) | ((digest[2] & 0xFF) << 16) | ((digest[1] & 0xFF) << 8) | (digest[0] & 0xFF); } catch (Exception e) { throw new RuntimeException("MD5 Hash error", e); } } public static void main(String[] args) { ConsistentHashing consistentHashing = new ConsistentHashing(); // 添加服务器节点 consistentHashing.addServer("Server1"); consistentHashing.addServer("Server2"); consistentHashing.addServer("Server3"); // 模拟请求的键 String[] keys = {"Key1", "Key2", "Key3", "Key4", "Key5"}; // 查找键对应的服务器 for (String key : keys) { System.out.println("Key: " + key + " is mapped to " + consistentHashing.getServer(key)); } } }
代码解释:
- 虚拟节点:每个服务器(
Server1
,Server2
,Server3
)在哈希环上映射了多个虚拟节点。这样做是为了避免数据倾斜问题,提高负载均衡。 - 哈希环:我们使用
TreeMap
来存储哈希值,TreeMap
自动排序,方便我们找到合适的节点。 - 查找服务器节点:当查找一个键对应的服务器时,我们先计算它的哈希值,然后在哈希环上找到大于等于该哈希值的第一个节点。如果没有,则循环回到哈希环的第一个节点。
一致性哈希的优点:
- 节点扩展或减少时只影响少量数据:当新增或删除服务器节点时,只有少量数据需要重新分配,而不是全部数据。
- 负载均衡:虚拟节点帮助均衡每个物理节点的负载。
总结:
- 普通哈希:适用于节点固定且不变动的简单场景,扩展性差,所有数据都要重新分配。
- 一致性哈希:适合分布式系统中动态节点变动的场景,扩展性好,容错性强,适合如分布式缓存、存储等系统。
原文地址:https://blog.csdn.net/ykp92/article/details/142367565
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!