一致性HASH算法

给你一囗甜甜゛ 提交于 2019-12-29 19:56:16

【推荐】2019 Java 开发者跳槽指南.pdf(吐血整理) >>>

一致性HASH算法研究

1.引言

在研究Ceph CRUSH算法时,看到有文章说它是一种特殊的一致性HASH算法,于是我便开始研究一致性HASH算法做先期准备,发现理念确实接近,区别在于虚拟节点和物理节点的映射办法不同,这是Ceph的核心算法,非常关键,此处不表,下文分解。

2.一致性HASH的出现背景及其优势

在分布式系统中,常常利用HASH算法进行数据分布,目的是希望将数据均匀的分布到各节点,分担压力,尤其是在缓存系统中。一个典型的设计如下: 图一

2.1场景说明

为了提升系统性能,解决数据库的性能瓶颈,设置N个缓存服务器,将应用和数据库进行隔离,每个缓存服务器负责数据库1/N的数据。应用程序在访问数据时,根据关键字计算得到hash值,并根据一定规则找到数据所在的缓存节点。

2.2最简单模型

最简单的办法是,采用hash(key) mode N的办法计算文件落在的服务器位置,那么在Find Cache Server步骤中,需要增加一个取摸操作,存在的问题是:
集群添加机器,计算公式变为hash(key) / (N + newAddedCount)
集群退出机器,计算公式变为hash(key) / (N - removedCount)
由于计算公式的分母变化,计算值也都将发生变化。通过取摸运算,找到的节点错误,缓存将失效。我们需要一种方法,当对集群扩容,或者从集群中移除失效机器时,只会导致少量的数据失效,可以很快的在正常的机器或者新增的机器上重新构建起缓存。

2.3 一致性HASH模型

基于这个想法,一些专业人士更提出了明确的要求,并形成论文 一致性HASH论文

2.3.1 一致性HASH基本设计思路

一致性哈希构造了一个hash环,将服务器节点映射到环上,将obj也映射到环上,将他们至于同一个空间中(0~2^32-1),针对每一个对象,顺时针查找第一个>=hash(obj)的节点,就是它要存放的目标系统,如果没有找到,按照顺时针,就回归到第一个服务器。

![输入图片说明 在上图实例中,根据一致性HASH规则,分配后的结果如下:
obj1 ~ obj3归属于Cache Server B;
obj4 ~ obj7归属于Cache Server C;
obj8~obj11归属于Cache Server A.正是按照上述规则分析下来的结果。

但是存在一个数据分布不均衡的问题,在下图中可以看到,大量的数据会落在服务器A上,但是B,C上只有少量数据。数据分布明显不平衡。

输入图片说明

为了解决数据不均衡的问题,一致性HASH中引入了虚拟节点,将对象均匀的映射到虚拟节点,再将虚拟节点映射到物理节点。

输入图片说明

通过设置大量的虚拟节点,将数据平均分布到虚拟节点上,最终达到平均分布到物理节点上的效果。此处的关键点是:通过为每个物理节点配置虚拟节点,所有的虚拟节点可以平均的散布到hash环上此处一看使用的hash函数;另外看物理节点设置的虚拟节点数量;

此处,一致性hash是符合我们的期望的:

1、平衡性(Balance):hash后的结果能够平均分布,比如在存储中,数据可以平均分布到各节点,不会出现个别节点数据量特别少,个别特别多的情况;
2、单调性(Monotonicity):当新增或者移除节点时,要么映射到原来的位置,或者映射到新节点;不会映射到无效节点;

2.3.2 数据分布均衡测试

为了测试一致性hash算法的特性以及虚拟节点对数据分布平衡的影响,我用C++实现了一个一致性hash算法,进行统计试验。

  1. 在相同的测试数据,相同的物理节点下,测试不同虚拟节点数量下,数据的分布情况: 测试样本:10000条URL记录,用作对象名称,作为hash函数的输入 采用的hash函数:c++11中默认的std::hash() 数据中虚拟节点数量参数是指每个物理节点对应的虚拟节点数量。
1节点
101.71.4.31:80  764
101.71.4.32:80  2395
101.71.4.33:80  1478
101.71.4.34:80  786
101.71.4.35:80  4577

10节点
101.71.4.31:80  1139
101.71.4.32:80  4862
101.71.4.33:80  1484
101.71.4.34:80  1243
101.71.4.35:80  1272

100节点
101.71.4.31:80  2646
101.71.4.32:80  2576
101.71.4.33:80  1260
101.71.4.34:80  705
101.71.4.35:80  2813

512虚拟节点
101.71.4.31:80  2015
101.71.4.32:80  2038
101.71.4.33:80  1948
101.71.4.34:80  2128
101.71.4.35:80  1871

1024虚拟节点
101.71.4.31:80  2165
101.71.4.32:80  1389
101.71.4.33:80  2045
101.71.4.34:80  2123
101.71.4.35:80  2278

2048节点
101.71.4.31:80  1976
101.71.4.32:80  1939
101.71.4.33:80  1892
101.71.4.34:80  2164
101.71.4.35:80  2029


4096节点
101.71.4.31:80  1972
101.71.4.32:80  2139
101.71.4.33:80  2095
101.71.4.34:80  1879
101.71.4.35:80  1915

从数据可以看到,随着对应的虚拟节点越来越多,数据的分布也越来越平衡,但是虚拟节点到达一定数量后,到达了瓶颈,毕竟不可能实现绝对的平衡。

2.通过增加或者移除节点,查看数据的移动情况: 移除节点 采用1024虚拟节点的情况下,使32节点失效,得到如下的统计结果:

101.71.4.31:80  2392
101.71.4.33:80  2374
101.71.4.34:80  2635
101.71.4.35:80  2599

我对记录文件进行了对比,发现31,33,34,35原来各自属于自身的数据没变,多出来的是32上的数据,被平均分布到了还有效的四台机器。

增加节点,采用1024虚拟节点的情况下,增加36节点,得到如下结果:

101.71.4.31:80  1726
101.71.4.32:80  1271
101.71.4.33:80  1776
101.71.4.34:80  1743
101.71.4.35:80  1768
101.71.4.36:80  1716

我对记录文件进行了对比,发现31,32,33,34,35上的数据还是以前属于他的数据,但是各自有部分迁移到了36机器上。

这两个实验都说明了一致性Hash的单调性。

2.3.3 一致性hash算法实现

//构建带虚拟节点的hash环,对每个真实的物理节点,配置若干虚拟节点,并进行排序
for (RNode node : rnodes)
{
	for (int i = 1; i <= virtual_node_count; i++)
	{
		VNode vnode;
		vnode.ip_port = node.ip_port + "#" + to_string(i);
		vnode.id = myhash(vnode.ip_port);  //虚拟节点在hash环上的映射
		circle.push_back(vnode);
		node.virtual_nodes.push_back(vnode);
	}
}

sort(circle.begin(), circle.end(), cmpVNode);

//计算每个URL落在那个虚拟节点
VNode getLocation(string url, vector<VNode>& vnodes)
{
	VNode tmp;
	tmp.id = myhash(url.c_str());
	vector<VNode>::iterator iter =  std::lower_bound(vnodes.begin(), vnodes.end(), tmp, cmpVNode);

	if (iter == vnodes.end())
	{
		return vnodes[0];
	}else 
	{
		return *iter;
	}
}

//根据虚拟节点,找到对应物理节点
string real_node = getRealNodeInfo(vnode);

3.CRUSHMAP的进一步研究

研究一致性HASH的目的是为了更好的理解Ceph CRUSHMAP算法,此处简单说明Ceph中对象是如何映射到具体设备的某块硬盘上。 在Ceph中,每个对象都属于某个PG,我把这些PG理解为一致性哈希中的虚拟节点,目的是为了让对象分布更均匀。 而pg是如何映射到OSD呢。在上文中,这种映射关系比较简单,就是多对一,在ceph中则比较复杂,因为映射关系依赖于集群的拓扑结构,而每个对象都还有多副本,需要指定的映射算法,计算出pg所在的主OSD以及副本OSD。

输入图片说明 我会在以后的博客中,结合论文,说明obj和pg的映射关系,以及pg和osd之间是如何做映射的。

附录

一致性HASH算法应用之广非常广泛,在分布式存储,缓存系统中会用到,在nginx的负载均衡中也会用到,理解一致性HASH算法,对理解分布式系统中的实现机制非常有帮助。下文是一致性hash更深入的文章,可研究之。

nginx中一致性HASH的使用
数据库分表中的一致性HASH
Ceph数据分布:CRUSH算法与一致性Hash - shanno
亚马逊的Dynamo系统
jump Consistent hash:零内存消耗,均匀,快速,简洁,来自Google的一致性哈希算法

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!