背景:

一致性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简单介绍和使用的更多相关文章

  1. 一致性Hash算法介绍(分布式环境算法)

    32的整数环(这个环被称作一致性Hash环),根据节点名称的Hash值(其分布范围同样为0~232)将节点放置在这个Hash 环上.然后根据KEY值计算得到其Hash值(其分布范围也同样为0~232  ...

  2. hash简单介绍

    hash也称"散列", 是一种基于映射关系的存储方式,将任意长度的二进制值输出为固定长度的较小的二进制值,这种输出的小的固定长度的值为hash值: 1. 散列技术是在关键字key与 ...

  3. 给面试官讲明白:一致性Hash的原理和实践

    "一致性hash的设计初衷是解决分布式缓存问题,它不仅能起到hash作用,还可以在服务器宕机时,尽量少地迁移数据.因此被广泛用于状态服务的路由功能" 01分布式系统的路由算法 假设 ...

  4. 一致性Hash算法原理,java实现,及用途

    学习记录: 一致性Hash算法原理及java实现:https://blog.csdn.net/suifeng629/article/details/81567777 一致性Hash算法介绍,原理,及使 ...

  5. 11.redis cluster的hash slot算法和一致性 hash 算法、普通hash算法的介绍

    分布式寻址算法 hash 算法(大量缓存重建) 一致性 hash 算法(自动缓存迁移)+ 虚拟节点(自动负载均衡) redis cluster 的 hash slot 算法 一.hash 算法 来了一 ...

  6. 负载均衡算法: 简单轮询算法, 平滑加权轮询, 一致性hash算法, 随机轮询, 加权随机轮询, 最小活跃数算法(基于dubbo) java代码实现

    直接上干活 /** * @version 1.0.0 * @@menu <p> * @date 2020/11/17 16:28 */ public class LoadBlance { ...

  7. LB中使用到的一致性Hash算法的简单实现

    1.类的Diagram 2.代码实现 2.1.Node类,每个Node代表集群里面的一个节点或者具体说是某一台物理机器: package consistencyhash; import lombok. ...

  8. 转载自lanceyan: 一致性hash和solr千万级数据分布式搜索引擎中的应用

    一致性hash和solr千万级数据分布式搜索引擎中的应用 互联网创业中大部分人都是草根创业,这个时候没有强劲的服务器,也没有钱去买很昂贵的海量数据库.在这样严峻的条件下,一批又一批的创业者从创业中获得 ...

  9. 一致性hash算法简介

    一致性哈希算法在1997年由麻省理工学院提出的一种分布式哈希(DHT)实现算法,设计目标是为了解决因特网中的热点(Hot spot)问题,初衷和CARP十分类似.一致性哈希修正了CARP使用的简单哈希 ...

随机推荐

  1. 写论文,关于word的技巧

    当段落的行间距为固定值的时候,图片会出现显示不全的情况,将行间距先改了再插入图片就没问题了. 从引用那边自动添加目录. ctrl+H打开可以使用通配符,改变字母或者数字的样式等

  2. hdu 6119 小小粉丝度度熊

    小小粉丝度度熊 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Sub ...

  3. 【bzoj4596】[Shoi2016]黑暗前的幻想乡 (矩阵树定理+容斥)

    Description 四年一度的幻想乡大选开始了,最近幻想乡最大的问题是很多来历不明的妖怪涌入了幻想乡,扰乱了幻想乡昔日的秩序.但是幻想乡的建制派妖怪(人类)博丽灵梦和八云紫等人整日高谈所有妖怪平等 ...

  4. hdu 5690(模运算)

    All X Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)Total Submi ...

  5. LeetCode OJ--Path Sum II **

    https://oj.leetcode.com/problems/path-sum-ii/ 树的深搜,从根到叶子,并记录符合条件的路径. 注意参数的传递,是否需要使用引用. #include < ...

  6. Intellij IDEA创建项目

    问题:无Java文件 解决方案: 打开file下面的project structure,在main目录下新建java文件,并将java文件设置为 Sources属性 问题:无resouces 解决方案 ...

  7. 洛谷——P2878 [USACO07JAN]保护花朵Protecting the Flowers

    P2878 [USACO07JAN]保护花朵Protecting the Flowers 题目描述 Farmer John went to cut some wood and left N (2 ≤ ...

  8. Network | NAT

    在计算机网络中,网络地址转换(Network Address Translation或简称NAT),也叫做网络掩蔽或者IP掩蔽(IP masquerading),是一种在IP封包通过路由器或防火墙时重 ...

  9. Arduino可穿戴教程ArduinoIDE新建编辑源文件

    Arduino可穿戴教程ArduinoIDE新建编辑源文件 Arduino IDE新建源文件 Arduino IDE启动后默认就新建了一个源文件,如图2.20所示.新建的源文件名称是以sketch_开 ...

  10. Springboot的Bean的Scope

    这周在项目中遇到这样一个Bug,代码大致是这样的,有一个LogEntity日志类,里面有一个InnerLog负责存储每次请求的RPCInfo相关信息, 每次请求的时候会把RPC相关信息加入到Inner ...