首页 > 编程知识 正文

treemap实现一致性哈希,哈希算法例题

时间:2023-05-03 16:26:31 阅读:50106 作者:203

分布式系统中对象与节点的映射关系。 在传统的方法中,对象的俏皮鱼值用于建模节点的数量,并将节点映射到相应编号的节点。 在这种方式中,当节点的个数变动时,大多数对象的映射关系变得无效,需要迁移。 另一方面,一致的俏皮话鱼算法在节点数变动时,禁用映射关系的对象非常少,迁移成本也非常小。 本文总结了一致的俏皮话鱼算法原理和Java实现,并列出了其应用。作者:寂寞大象http://www.Sina.com/https://kefeng.Wang/2018/08/10/consistent-hashing/3358 www.sefeng

1.1传统的俏皮鱼(ggdxxm )分布式系统假设有n个节点,传统方案使用mod ) key,n )映射数据和节点。

如果扩展容量或减小容量,则映射关系(即使只是增加或减少一个节点)为mod ) key,n 1)/mod ) key,n-1 ),大多数数据的映射关系将被禁用。

1.2一致性漂亮鱼1997年,麻省理工学院(MIT )的大卫卡勒格等6人发表了学术论文《Consistent hashing and random trees: distributed caching protocols for relieving hot spots on the World Wide Web(一致性俏皮的鱼和随机树:用于缓解万维网上热点的分布式缓存协议)》。 对于k个关键字和n个时隙) (分布式系统中的节点)的时髦鱼表,增减时隙的数量后,平均只需要K/n个关键字即可

1.3俏皮话鱼指标评价俏皮话鱼算法优劣,有以下指标,一致的俏皮话鱼都很满意:

均衡性(Balance )关键词俏皮鱼的住所均匀分布在住所空间,充分利用住所空间是设计俏皮鱼的基本特性之一。 单调性(Monotonicity ) :单调性是指地址空间增大时,还可以映射到由俏皮鱼函数得到的关键词俏皮鱼地址的新地址空间,而不仅限于原始地址空间。 或者即使地址空间减少,也只能映射到有效的地址空间。 简单的俏皮话鱼函数往往不能满足这一性质。 分布式(Spread ) :俏皮的鱼在分布式环境中很常见,最终用户通过俏皮的鱼函数将自己的内容存储在不同的缓冲区中。 此时,终端可能不能看到所有的缓冲器,只能看到其中的一部分。 如果终端想要在俏皮的鱼过程中将内容映射到缓冲器,由于终端看到的缓冲器范围可能不同,所以俏皮的鱼的结果不一致,最终相同的内容被终端映射到不同的缓冲器。 这显然是应该避免的,因为相同的内容存储在不同的缓冲区中,这会降低系统存储效率。 分散性的定义是上述情况发生的重要程度。 的俏皮话鱼算法应该能尽量避免矛盾,也就是说尽量降低分散性。 负载(Load ) :负载的问题实际上是从另一个角度来看分散性问题。 由于不同的终端可能会将相同的内容映射到不同的缓冲区,因此不同的用户可能会将特定缓冲区映射到不同的内容。 和分散性一样,这种情况也应该避免,所以一个好的打鱼算法应该能够最小化缓冲负荷。 1.4资料链接原论文《Consistent Hashing and Random Trees》链接如下。

官方链接- PDF版本本站复制- PDF版本相关论文《Web Caching with Consistent Hashing》链接如下:

官方链接- PDF版官方链接- HTM版本站副本-比pdf版更多的资料:

维基百科一致性散列

代码项目-一致散列

2算法原理2.1映射方案

2.1.1用俏皮的鱼函数和俏皮的鱼环设计俏皮的鱼函数hash(key ),要求取值范围为[ 0,2 ^ 32 ]

各俏皮鱼值在上图哈希环上分布,时钟12点位置为0,顺时针增加,接近12点左侧位置为2^32-1。

2.1.2节点(Node )映射为俏皮话的鱼环,如俏皮话鱼环上的绿球所示,由四个节点Node A/B/C/D、

相同Hash ()计算的结果是将IP地址或设备名称映射到俏皮的鱼环。

2.1.3对象(Object )四个对象对象对象对象对象对象对象a/b/c/d,如映射到俏皮鱼环的俏皮鱼环的焦唇颜色所示,

作为相同的Hash ()计算的结果,键值被映射到时髦的鱼圈上。

2.1.4对象(对象)节点映射)对象和节点映射到同一个俏皮的鱼环后,若要确定一个对象映射到哪个节点,请单击,

从这个对象中,只要沿着俏皮的鱼圈顺时针寻找,就能找到最初发现的节点,即。

可以看到,每个对象a/b/c/d都映射到节点a/b/c/d。

2.2删除节点的现实场景:在服务缩减时删除节点或节点关闭。 要删除节点Node C,请:

仅影响您要删除的节点(Nod )

e C)与上一个(顺时针为前进方向)节点(Node B)与之间的对象,也就是 Object C,
这些对象的映射关系,按照 2.1.4 的规则,调整映射至欲删除节点的下一个节点 Node D。
其他对象的映射关系,都无需调整。

2.3 增加节点

现实场景:服务器扩容时增加节点。比如要在 Node B/C 之间增加节点 Node X:
只会影响欲新增节点(Node X)与上一个(顺时针为前进方向)节点(Node B)与之间的对象,也就是 Object C,
这些对象的映射关系,按照 2.1.4 的规则,调整映射至新增的节点 Node X。
其他对象的映射关系,都无需调整。

2.4 虚拟节点

对于前面的方案,节点数越少,越容易出现节点在俏皮的鱼环上的分布不均匀,导致各节点映射的对象数量严重不均衡(数据倾斜);相反,节点数越多越密集,数据在俏皮的鱼环上的分布就越均匀。
但实际部署的物理节点有限,我们可以用有限的物理节点,虚拟出足够多的虚拟节点(Virtual Node),最终达到数据在俏皮的鱼环上均匀分布的效果:
如下图,实际只部署了2个节点 Node A/B,
每个节点都复制成3倍,结果看上去是部署了6个节点。
可以想象,当复制倍数为 2^32 时,就达到绝对的均匀,通常可取复制倍数为32或更高。
虚拟节点俏皮的鱼值的计算方法调整为:对“节点的IP(或机器名)+虚拟节点的序号(1~N)”作俏皮的鱼。

3 算法实现

一致性俏皮的鱼算法有多种具体的实现,包括 Chord 算法,KAD 算法等,都比较复杂。
这里给出一个简易实现及其演示,可以看到一致性俏皮的鱼的均衡性和单调性的优势。
单调性在本例中没有统计数据,但根据前面原理可知,增删节点后只有很少量的数据需要调整映射关系。

3.1 源码 /** * @author: https://kefeng.wang * @date: 2018-08-10 11:08 **/public class ConsistentHashing { // 物理节点 private Set<String> physicalNodes = new TreeSet<String>() { { add("192.168.1.101"); add("192.168.1.102"); add("192.168.1.103"); add("192.168.1.104"); } }; //虚拟节点 private final int VIRTUAL_COPIES = 1048576; // 物理节点至虚拟节点的复制倍数 private TreeMap<Long, String> virtualNodes = new TreeMap<>(); // 俏皮的鱼值 => 物理节点 // 32位的 Fowler-Noll-Vo 俏皮的鱼算法 // https://en.wikipedia.org/wiki/Fowler–Noll–Vo_hash_function private static Long FNVHash(String key) { final int p = 16777619; Long hash = 2166136261L; for (int idx = 0, num = key.length(); idx < num; ++idx) { hash = (hash ^ key.charAt(idx)) * p; } hash += hash << 13; hash ^= hash >> 7; hash += hash << 3; hash ^= hash >> 17; hash += hash << 5; if (hash < 0) { hash = Math.abs(hash); } return hash; } // 根据物理节点,构建虚拟节点映射表 public ConsistentHashing() { for (String nodeIp : physicalNodes) { addPhysicalNode(nodeIp); } } // 添加物理节点 public void addPhysicalNode(String nodeIp) { for (int idx = 0; idx < VIRTUAL_COPIES; ++idx) { long hash = FNVHash(nodeIp + "#" + idx); virtualNodes.put(hash, nodeIp); } } // 删除物理节点 public void removePhysicalNode(String nodeIp) { for (int idx = 0; idx < VIRTUAL_COPIES; ++idx) { long hash = FNVHash(nodeIp + "#" + idx); virtualNodes.remove(hash); } } // 查找对象映射的节点 public String getObjectNode(String object) { long hash = FNVHash(object); SortedMap<Long, String> tailMap = virtualNodes.tailMap(hash); // 所有大于 hash 的节点 Long key = tailMap.isEmpty() ? virtualNodes.firstKey() : tailMap.firstKey(); return virtualNodes.get(key); } // 统计对象与节点的映射关系 public void dumpObjectNodeMap(String label, int objectMin, int objectMax) { // 统计 Map<String, Integer> objectNodeMap = new TreeMap<>(); // IP => COUNT for (int object = objectMin; object <= objectMax; ++object) { String nodeIp = getObjectNode(Integer.toString(object)); Integer count = objectNodeMap.get(nodeIp); objectNodeMap.put(nodeIp, (count == null ? 0 : count + 1)); } // 打印 double totalCount = objectMax - objectMin + 1; System.out.println("======== " + label + " ========"); for (Map.Entry<String, Integer> entry : objectNodeMap.entrySet()) { long percent = (int) (100 * entry.getValue() / totalCount); System.out.println("IP=" + entry.getKey() + ": RATE=" + percent + "%"); } } public static void main(String[] args) { ConsistentHashing ch = new ConsistentHashing(); // 初始情况 ch.dumpObjectNodeMap("初始情况", 0, 65536); // 删除物理节点 ch.removePhysicalNode("192.168.1.103"); ch.dumpObjectNodeMap("删除物理节点", 0, 65536); // 添加物理节点 ch.addPhysicalNode("192.168.1.108"); ch.dumpObjectNodeMap("添加物理节点", 0, 65536); }} 3.2 复制倍数为 1 时的均衡性

修改代码中 VIRTUAL_COPIES = 1(相当于没有虚拟节点),运行结果如下(可见各节点负荷很不均衡):

======== 初始情况 ========IP=192.168.1.101: RATE=45%IP=192.168.1.102: RATE=3%IP=192.168.1.103: RATE=28%IP=192.168.1.104: RATE=22%======== 删除物理节点 ========IP=192.168.1.101: RATE=45%IP=192.168.1.102: RATE=3%IP=192.168.1.104: RATE=51%======== 添加物理节点 ========IP=192.168.1.101: RATE=45%IP=192.168.1.102: RATE=3%IP=192.168.1.104: RATE=32%IP=192.168.1.108: RATE=18% 3.2 复制倍数为 32 时的均衡性

修改代码中 VIRTUAL_COPIES = 32,运行结果如下(可见各节点负荷比较均衡):

======== 初始情况 ========IP=192.168.1.101: RATE=29%IP=192.168.1.102: RATE=21%IP=192.168.1.103: RATE=25%IP=192.168.1.104: RATE=23%======== 删除物理节点 ========IP=192.168.1.101: RATE=39%IP=192.168.1.102: RATE=37%IP=192.168.1.104: RATE=23%======== 添加物理节点 ========IP=192.168.1.101: RATE=35%IP=192.168.1.102: RATE=20%IP=192.168.1.104: RATE=23%IP=192.168.1.108: RATE=20% 3.2 复制倍数为 1M 时的均衡性

修改代码中 VIRTUAL_COPIES = 1048576,运行结果如下(可见各节点负荷非常均衡):

======== 初始情况 ========IP=192.168.1.101: RATE=24%IP=192.168.1.102: RATE=24%IP=192.168.1.103: RATE=25%IP=192.168.1.104: RATE=25%======== 删除物理节点 ========IP=192.168.1.101: RATE=33%IP=192.168.1.102: RATE=33%IP=192.168.1.104: RATE=33%======== 添加物理节点 ========IP=192.168.1.101: RATE=25%IP=192.168.1.102: RATE=24%IP=192.168.1.104: RATE=24%IP=192.168.1.108: RATE=24% 4 应用

一致性俏皮的鱼是分布式系统组件负载均衡的首选算法,它既可以在客户端实现,也可以在中间件上实现。其应用有:

分布式散列表(DHT)的设计;分布式关系数据库(MySQL):分库分表时,计算数据与节点的映射关系;分布式缓存:Memcached 的客户端实现了一致性俏皮的鱼,还可以使用中间件 twemproxy 管理 redis/memcache 集群;RPC 框架 Dubbo:用来选择服务提供者;亚马逊的云存储系统 Dynamo;分布式 Web 缓存;Bittorrent DHT;LVS。

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。