一致性hash(1)

文章介绍了普通哈希在缓存中的局限性,特别是在节点增加或删除后可能导致命中率大幅下降。一致性哈希算法通过环形结构和映射机制,有效地解决了这个问题。作者还演示了如何在实际测试中应用一致性哈希并分析其性能变化。
摘要由CSDN通过智能技术生成
  • @return

*/

public Long hash(String key);

/**

  • 模拟意外情况断掉一个节点,用于测试缓存命中率

  • @param node

*/

public void removeNodeUnexpected(Node node);

}

普通hash的实现非常简单,代码如下

package cn.enjoy.hash;

import java.util.ArrayList;

import java.util.List;

import java.util.zip.CRC32;

public class NormalHashNodeServiceImpl implements HashNodeService{

/**

  • 存储节点列表

*/

private List nodes = new ArrayList<>();

@Override

public void addNode(Node node) {

this.nodes.add(node);

}

@Override

public Node lookupNode(String key) {

long k = hash(key);

int index = (int) (k % nodes.size());

return nodes.get(index);

}

@Override

public Long hash(String key) {

CRC32 crc32 = new CRC32();

crc32.update(key.getBytes());

return crc32.getValue();

}

@Override

public void removeNodeUnexpected(Node node) {

nodes.remove(node);

}

}

好,有什么问题呢,测试一下

package cn.enjoy.hash;

import cn.enjoy.hash.HashNodeService;

import cn.enjoy.hash.Node;

import cn.enjoy.hash.NormalHashNodeServiceImpl;

import org.junit.Before;

import org.junit.Test;

import java.util.HashMap;

import java.util.Map;

public class HashTest {

private HashNodeService nodeService;

@Before

public void init() {

nodeService= new NormalHashNodeServiceImpl();

Node addNode1 = new Node(“node1”, “192.168.0.11”);

Node addNode2 = new Node(“node2”, “192.168.0.12”);

Node addNode3 = new Node(“node3”, “192.168.0.13”);

Node addNode4 = new Node(“node4”, “192.168.0.14”);

Node addNode5 = new Node(“node5”, “192.168.0.15”);

Node addNode6 = new Node(“node6”, “192.168.0.16”);

Node addNode7 = new Node(“node7”, “192.168.0.17”);

Node addNode8 = new Node(“node8”, “192.168.0.18”);

nodeService.addNode(addNode1);

nodeService.addNode(addNode2);

nodeService.addNode(addNode3);

nodeService.addNode(addNode4);

nodeService.addNode(addNode5);

nodeService.addNode(addNode6);

nodeService.addNode(addNode7);

nodeService.addNode(addNode8);

}

@Test

public void test2() {

//用于检查数据分布情况

Map<String, Integer> countmap = new HashMap<>();

Node node = null;

for (int i = 1; i <= 100000; i++) {

String key = String.valueOf(i);

node = nodeService.lookupNode(key);

node.cacheString(key, “TEST_VALUE”);

String k = node.getIp();

Integer count = countmap.get(k);

if (count == null) {

count = 1;

countmap.put(k, count);

} else {

count++;

countmap.put(k, count);

}

}

System.out.println(“初始化数据分布情况:” + countmap);

// 正常情况下的去获取数据,命中率

int hitcount = 0;

for (int i = 1; i <= 100000; i++) {

String key = String.valueOf(i);

node = nodeService.lookupNode(key);

if (node != null) {

String value = node.getCacheValue(key);

if (value != null) {

hitcount++;

}

}

}

double h = Double.parseDouble(String.valueOf(hitcount))/ Double.parseDouble(String.valueOf(100000));

System.out.println(“初始化缓存命中率:”+ h);

Node addNode9 = new Node(“node9”, “192.168.0.19”);

nodeService.addNode(addNode9);

hitcount = 0;

for (int i = 1; i <= 100000; i++) {

String key = String.valueOf(i);

node = nodeService.lookupNode(key);

if (node != null) {

String value = node.getCacheValue(key);

if (value != null) {

hitcount++;

}

}

}

h = Double.parseDouble(String.valueOf(hitcount))/ Double.parseDouble(String.valueOf(100000));

System.out.println(“增加一个节点后缓存命中率:”+ h);
}

}

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

本来8个服务器(node),使用hash算法从集群里取值命中率100%,当新增了一个节点后可怜兮兮,命中率直接变成了10%左右,这个时候普通hash的问题显露无疑了,怎么办,试试一致性hash.

1.1.2. 一致性hash算法

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

一致性hash是为了解决普通hash的问题,使用一个环状结构,具体可以分层4步骤。

1.1.2.1. 步骤

1.1.2.1.1. 构建环形hash空间

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

说明:

一致性hash算法通过一个叫作一致性hash环的数据结构实现

这个环的起点是0,终点是2^32 - 1,并且起点与终点连接,环的中间的整数按逆时针分布

这个环的整数分布范围是[0, 2^32-1]

1.1.2.1.2. 把对象映射到hash空间

假设现在我们有4个对象,分别为o1,o2,o3,o4,

使用hash函数计算这4个对象的hash值(范围为0 ~ 2^32-1)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

1.1.2.1.3. 把cache节点映射到hash空间

使用同样的hash函数,我们将机器也放置到hash环上。

假设我们有三台缓存机器,分别为 c1,c2,c3

使用hash函数计算这3台机器的hash值

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

1.1.2.1.4. 对象映射到cache节点

将对象和机器都放置到同一个hash环后

在hash环上顺时针查找距离这个对象的hash值最近的机器,即是这个对象所属的机器。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

1.1.2.1.5. 增加一个节点的情况

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

增加机器c4的部署并将机器c4加入到hash环的机器c3与c2之间。

只有对象o4被重新分配到了c4

其他对象仍在原有机器上

1.1.2.1.6. 删除一个节点的情况

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

将机器c1下线(当然,也有可能是机器c1宕机)

只有对象o2被重新分配到机器c3

其他对象仍在原有机器上

1.1.2.1.7. 虚拟节点

仅仅使用真实的节点来形成环的结构可能会导致数据的倾斜,为了解决这种数据不对称的现象,可以在原理缓存的机器的基础上,增加相应的虚拟节点,这样数据就会均匀的打散到其他节点中。

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

img

img

img

img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)

最后

只要是程序员,不管是Java还是Android,如果不去阅读源码,只看API文档,那就只是停留于皮毛,这对我们知识体系的建立和完备以及实战技术的提升都是不利的。

真正最能锻炼能力的便是直接去阅读源码,不仅限于阅读各大系统源码,还包括各种优秀的开源库。

腾讯、字节跳动、阿里、百度等BAT大厂 2019-2021面试真题解析

资料太多,全部展示会影响篇幅,暂时就先列举这些部分截图

《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门即可获取!

CoPEF.jpg" />

最后

只要是程序员,不管是Java还是Android,如果不去阅读源码,只看API文档,那就只是停留于皮毛,这对我们知识体系的建立和完备以及实战技术的提升都是不利的。

真正最能锻炼能力的便是直接去阅读源码,不仅限于阅读各大系统源码,还包括各种优秀的开源库。

[外链图片转存中…(img-V61tqcHi-1712339670145)]

腾讯、字节跳动、阿里、百度等BAT大厂 2019-2021面试真题解析

[外链图片转存中…(img-lSIstF8E-1712339670146)]

资料太多,全部展示会影响篇幅,暂时就先列举这些部分截图

《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门即可获取!
  • 4
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值