一致性hash

/**

  • 存储节点列表

*/

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. 虚拟节点

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

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

这个时候客户端进行数据存取的时候找的就是虚拟节点,而不是真实的物理节点服务器。

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

这样增加节点会删除节点虽然命中率会降低,却很好的解决了数据倾斜问题。

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

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

其他对象仍在原有机器上

1.1.2.1.7. 虚拟节点

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

[外链图片转存中…(img-b374zjpf-1716912834796)]

这个时候客户端进行数据存取的时候找的就是虚拟节点,而不是真实的物理节点服务器。

[外链图片转存中…(img-avZzQsYp-1716912834797)]

这样增加节点会删除节点虽然命中率会降低,却很好的解决了数据倾斜问题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值