一致性Hash简介和使用

背景:

一致性Hash用于分布式缓存系统,将Key值映射到具体机器Ip上,并且增加和删除1台机器的数据移动量较小,对现网影响较小

实现:

1 Hash环:将节点的Hash值映射到一个Hash环中,每个Key顺时针第一个找到的节点,就是这个Key被路由到的机器

2 "虚拟节点":将节点虚拟成多个"虚拟节点"分布在Hash环上,使得分布更均匀,扩缩容影响较小

代码实例:

/*
 * @ 一致性Hash模拟测试
 * @ 结论:模拟4台机器扩容1台,遍历Key[0,999983]
		- 一致性Hash需移动181161个Key,约占18%(1/5左右,符合预期效果)
		- 取模Hash需移动799984个Key,约占80%
 * @ 2014.05.30
 */

#include <stdint.h>
#include <iostream>
#include <string.h>
#include <sstream>
#include <map>
#include <vector>
using namespace std;

#define HASH_MOD (999983) 

template <class T>
string ToStr(const T &t)
{
	stringstream stream;
	stream << t;
	return stream.str();
}

uint32_t APHash(string &sKey)
{
	char *key = (char*)sKey.c_str();
	unsigned int hash = 0;
	for (int i=0; *key; i++)
	{
		if ((i & 1) == 0) {
			hash ^= ((hash<<7)^(*key++)^(hash>>3));
		} else {
			hash ^= (~((hash<<11)^(*key++)^(hash>>5)));
		}
	}
	return hash%HASH_MOD;
}

class CMyConHash
{
public:
	/* 添加一台机器(IP) */
	void AddIp(const string &sIp)
	{
		// 每个IP分配128个虚拟节点,原因:结合APHash实验结果分布较均匀
		for (int i = 0; i < 128; i ++)
		{
			string sCode = sIp + ToStr(i) + "#Hash";
			uint32_t uVirKey = APHash(sCode);
			mapVirKey2Ip[uVirKey] = sIp;
			mapIp2VirKey[sIp].push_back(uVirKey);
		}
	}

	/* 删除一台机器(IP) */
	void DelIp(const string &sIp)
	{
		if (mapIp2VirKey.count(sIp) == 0) {
			cout << "DelIp Err: mapIp2VirKey Don`t Has Ip=" << sIp << endl;
			return;
		}
		vector<uint32_t> vecVirKey = mapIp2VirKey[sIp];
		for (int i = 0; i < vecVirKey.size(); i ++)
		{
			uint32_t uVirKey = vecVirKey[i];
			if (mapVirKey2Ip[uVirKey] == sIp) {
				// 得判断下,有可能2个IP虚拟节点相等后覆盖了
				mapVirKey2Ip.erase(uVirKey);
			}
		}
		mapIp2VirKey.erase(sIp);
	}

	/* 路由:给每个Key找到负责的机器(IP) */
	int FindIp(uint32_t uKey, string &sIp)
	{
		if (mapVirKey2Ip.size() == 0) {
			cout << "FindIp Err: mapVirKey2Ip.size() == 0" << endl;
			return -1;
		}
		bool bFind = false;
		uint32_t uVirKey;
		map<uint32_t, string>::iterator iter;
		// 遍历std::map是按Key大小顺序输出(区别std::tr1::unordered_map)
		for(iter = mapVirKey2Ip.begin(); iter != mapVirKey2Ip.end(); iter ++)
		{
			uVirKey = iter->first;
			if (uVirKey > uKey%HASH_MOD) {
				sIp = iter->second;
				bFind = true;
				break;
			}
		}
		if (!bFind) {
			// 找不到比Key小的虚拟节点,故使用最小的虚拟节点(环)
			iter = mapVirKey2Ip.begin();
			uVirKey = iter->first;
			sIp = iter->second;
		}
		//cout << "FindIp Suc:" << uKey%HASH_MOD << "=>" << uVirKey << "," << sIp << endl;
		return 0;
	}

	/* 打印各个IP负责的Key区域大小,影响因素:1 Hash函数 2 虚拟节点个数 */
	/* 4台机器的情况,相对还是较均匀:
		Ip=202.168.14.241,Cnt=251649
		Ip=202.168.14.242,Cnt=257902
		Ip=202.168.14.243,Cnt=245945
		Ip=202.168.14.244,Cnt=235516 */
	void EchoIpState()
	{
		map<string, uint32_t> mapIpCnt;
		map<uint32_t, string>::iterator iter = mapVirKey2Ip.end();
		iter --;
		uint32_t uPreKey = iter->first;
		string sPreIp = iter->second;
		do {
			iter --;
			uint32_t uVirKey = iter->first;
			string sIp = iter->second;
			if (mapIpCnt.count(sPreIp) == 0) {
				mapIpCnt[sPreIp] = uPreKey-uVirKey;
			} else {
				mapIpCnt[sPreIp] += uPreKey-uVirKey;
			}
			uPreKey = uVirKey;
			sPreIp = sIp;
		} while (iter != mapVirKey2Ip.begin()); 

		cout << "Ip Size=" << mapIpCnt.size() << endl;
		map<string, uint32_t>::iterator iter1;
		for(iter1 = mapIpCnt.begin(); iter1 != mapIpCnt.end(); iter1 ++)
		{
			cout << "Ip=" << iter1->first << ",Cnt=" << iter1->second << endl;
		}
	}
private:
	map< uint32_t, string > mapVirKey2Ip;
	map< string, vector<uint32_t> > mapIp2VirKey;
};

class CMyModHash
{
public:
	void AddIp(const string &sIp)
	{
		vecIpList.push_back(sIp);
	}
	void FindIp(uint32_t uKey, string &sIp)
	{
		sIp = vecIpList[uKey%vecIpList.size()];
	}
	void EchoIpState()
	{
		cout << "Ip Cnt=" << vecIpList.size() << endl;
	}
private:
	vector<string> vecIpList;
};

int main()
{
	CMyConHash oMyHash;
	// CMyModHash oMyHash;

	// 模拟初始化4台机器
	oMyHash.AddIp("202.168.14.241");
	oMyHash.AddIp("202.168.14.242");
	oMyHash.AddIp("202.168.14.243");
	oMyHash.AddIp("202.168.14.244");
	oMyHash.EchoIpState();

	// 保存下各个Key路由的机器
	string sIp, arrKeyIp[HASH_MOD];
	for (uint32_t key = 0; key < HASH_MOD; key ++)
	{
		oMyHash.FindIp(key, sIp);
		arrKeyIp[key] = sIp;
	}

	// 模拟添加1台机器
	oMyHash.AddIp("202.168.14.245");
	oMyHash.EchoIpState();

	// 判断多少Key对应数据需要移动机器
	uint32_t uCnt = 0;
	for (uint32_t key = 0; key < HASH_MOD; key ++)
	{
		oMyHash.FindIp(key, sIp);
		if (arrKeyIp[key] != sIp) {
			uCnt ++;
		}
	}
	cout << "Key Sum=" << HASH_MOD << " , Need To Move:" << uCnt << endl;

	return 0;
}

一致性Hash简介和使用

时间: 2024-12-14 21:22:23

一致性Hash简介和使用的相关文章

一致性hash算法简介与代码实现

一.简介: 一致性hash算法提出了在动态变化的Cache环境中,判定哈希算法好坏的四个定义: 1.平衡性(Balance) 2.单调性(Monotonicity) 3.分散性(Spread) 4.负载(Load) 普通的哈希算法(也称硬哈希)采用简单取模的方式,将机器进行散列,这在cache环境不变的情况下能取得让人满意的结果,但是当cache环境动态变化时,这种静态取模的方式显然就不满足单调性的要求(当增加或减少一台机子时,几乎所有的存储内容都要被重新散列到别的缓冲区中). 一致性哈希算法的

Memecached缓存原理及基本操作、分布式(一致性hash)

原文地址:http://lixiangfeng.com/blog/article/content/7869717 转载请标明此处,谢谢! 缓存是什么?为什么要使用缓存? 缓存,通过在内存中缓存数据和对象来减少读取数据库的次数,从而提供动态.数据库驱动网站的速度. 缓存工具有哪些?区别在哪里? 缓存工具:Memecached.redis.MongoDB 区别: 性能都比较高:总体来讲,TPS(每秒总事务量)方面redis和memcache差不多,要大于 mongodb: 操作的便利性: a)   

分布式memcached学习(四)&mdash;&mdash; 一致性hash算法原理

    分布式一致性hash算法简介 当你看到"分布式一致性hash算法"这个词时,第一时间可能会问,什么是分布式,什么是一致性,hash又是什么.在分析分布式一致性hash算法原理之前,我们先来了解一下这几个概念. 分布式 分布式(distributed)是指在多台不同的服务器中部署不同的服务模块,通过远程调用协同工作,对外提供服务. 以一个航班订票系统为例,这个航班订票系统有航班预定.网上值机.旅客信息管理.订单管理.运价计算等服务模块.现在要以集中式(集群,cluster)和分布

【数据结构与算法】一致性Hash算法及Java实践

追求极致才能突破极限 一.案例背景 1.1 系统简介 首先看一下系统架构,方便解释: 页面给用户展示的功能就是,可以查看任何一台机器的某些属性(以下简称系统信息). 消息流程是,页面发起请求查看指定机器的系统信息到后台,后台可以查询到有哪些server在提供服务,根据负载均衡算法(简单的轮询)指定由哪个server进行查询,并将消息发送到Kafka,然后所有的server消费Kafka的信息,当发现消费的信息要求自己进行查询时,就连接指定的machine进行查询,并将结果返回回去. Server

分布式缓存技术memcached学习系列(四)—— 一致性hash算法原理

文章主目录 分布式一致性hash算法简介 分布式一致性hash算法使用背景 环形hash空间 映射key到环形hash空间 映射server节点到hash空间 映射key到server节点 添加server节点 删除server节点 虚拟节点的引入 节点变化数据分流的问题 一致性hash算法与取模算法的比较 参考文档 回到顶部 分布式一致性hash算法简介 当你看到“分布式一致性hash算法”这个词时,第一时间可能会问,什么是分布式,什么是一致性,hash又是什么.在分析分布式一致性hash算法

【高并发解决方案】7、一致性hash解读

一致性哈希算法在1997年由麻省理工学院提出的一种分布式哈希(DHT)实现算法,设计目标是为了解决因特网中的热点(Hot spot)问题,初衷和CARP十分类似.一致性哈希修正了CARP使用的简 单哈希算法带来的问题,使得分布式哈希(DHT)可以在P2P环境中真正得到应用. 一致性hash算法提出了在动态变化的Cache环境中,判定哈希算法好坏的四个定义: 1.平衡性(Balance):平衡性是指哈希的结果能够尽可能分布到所有的缓冲中去,这样可以使得所有的缓冲空间都得到利用.很多哈希算法都能够满

一致性hash算法

一致性哈希算法在1997年由麻省理工学院提出的一种分布式哈希(DHT)实现算法,设计目标是为了解决因特网中的热点(Hot spot)问题,初衷和CARP十分类似.一致性哈希修正了CARP使用的简单哈希算法带来的问题,使得分布式哈希(DHT)可以在P2P环境中真正得到应用. 一致性hash算法提出了在动态变化的Cache环境中,判定哈希算法好坏的四个定义: 1.平衡性(Balance):平衡性是指哈希的结果能够尽可能分布到所有的缓冲中去,这样可以使得所有的缓冲空间都得到利用.很多哈希算法都能够满足

分布式算法(一致性Hash算法)

一.分布式算法 在做服务器负载均衡时候可供选择的负载均衡的算法有很多,包括: 轮循算法(Round Robin).哈希算法(HASH).最少连接算法(Least Connection).响应速度算法(Response Time).加权法(Weighted )等.其中哈希算法是最为常用的算法. 典型的应用场景是: 有N台服务器提供缓存服务,需要对服务器进行负载均衡,将请求平均分发到每台服务器上,每台机器负责1/N的服务. 常用的算法是对hash结果取余数 (hash() mod N ):对机器编号

对一致性Hash算法,Java代码实现的深入研究

一致性Hash算法 关于一致性Hash算法,在我之前的博文中已经有多次提到了,MemCache超详细解读一文中"一致性Hash算法"部分,对于为什么要使用一致性Hash算法和一致性Hash算法的算法原理做了详细的解读. 算法的具体原理这里再次贴上: 先构造一个长度为232的整数环(这个环被称为一致性Hash环),根据节点名称的Hash值(其分布为[0, 232-1])将服务器节点放置在这个Hash环上,然后根据数据的Key值计算得到其Hash值(其分布也为[0, 232-1]),接着在