一致性HASH算法
一致性HASH算法的原理,还有不带虚拟结点的一致性HASH算法就不说了,直接谈一下带虚拟结点的一致性HASH算法,主要从实现的角度谈一下。
整个过程包括初始化环形数组,添加结点,删除结点,查找结点等等。
先看存储结构:
class ConsistentHash {
enum STATUS{ EMPTY=0,//初始化 REAL,//虚拟结点 EXT //虚拟结点顺时针方向扩展出的结点 }; struct Node { string m_nodeName;//虚拟结点名称 uint64_t m_hashKey;//虚拟结点对应的hash值 STATUS m_valid; //虚拟结点的状态 }; public: ConsistentHash(uint16_t count); ~ConsistentHash(){ };
void printConfigInfo(); bool removeNode(string node); bool addNode(vector<string> vnodes,string node); string findNode(const string &key);
void releaseExtNodes(Node &node,string &vnodeName,size_t vCount); void printcircle(); public: bool init();
uint64_t hash(const string &str,uint64_t &crckey); private: Node *m_vNode; /*虚拟结点到实际结点*/ unordered_map<uint64_t,string> m_v2eNode; /*实际结点到虚拟节点位置*/ /*实际结点名称,虚拟节点名称,虚拟节点在环上的位置 */ unordered_map<string,unordered_map<string,vector<size_t> > > m_node2v; uint16_t m_maxCount; }; |
整个结构没有复杂的数据结构,考虑到一致性HASH的应用场景,比如用到缓存代理之类的,这种应用很少会有用到添加、删除结点的情况,相反,更多的是每次访问都要做查询操作,查询操作远远多余修改操作。什么数据结构的查询效率最好呢,当然是HASH,也不是什么XX树之类的。
1, Init
(1)初始化m_vNode,
(2)将所有的M虚拟结点做HASH映射到m_vNode。
首次映射的虚拟结点m_valid是REAL
存储虚拟结点的HASH值与实际结点的对应关系,对应关系有多种算法,这里应用的是简单的也是最均衡的顺序对应算法,比如有10个虚拟结点,2个实际结点,第1,3,5,7,9虚拟结点就对应第1个实际结点,第2,4,6,8,0虚拟结点就对应第2个实际结点,
虚拟结点到实际结点的映射关系存储在m_v2eNode
(3)所有的虚拟结点顺时针扩展势力范围直到遇到下一个虚拟结点。
扩展的虚拟结点m_valid是EXT,其它都一样。
(4)记录第个实际结点对应那些虚拟结点,每个虚拟结点又在m_vNode中什么位置,主要是做增加和删除的时候会用到。存储数据在m_node2v
2, 查询
(1)用户输入key,转为一个HASH值,做为目的结点在m_vNode中的下标,从目的结点中取出hash值,查询m_vNode,即可得到实际结点,时间复杂度仅为o(1)。
3, 添加结点
(1) 输入为实际结点的名称,和其对应的一系列的虚拟结点名称。
(2) 映射虚拟结点到m_vNode。
映射的位置如果刚好有一个结点,并且m_valid是REAL,则要顺时针顺延到一个状态为EXT的结点,记录为A,主要是避免与现存结点冲突。
找到在m_vNode的位置后,给其赋值,更新m_v2eNode,
从m_vNode当前位置开始顺时针扩展势力范围,直到遇到一个状态为REAL的结点,记录为B,将A,B之关的位置从A对应的位置信息中移除,加入到新结点虚拟结点对应的位置信息中。
4, 删除结点
(1) 取出实际结点与虚拟结点及其位置的对应关系。
(2) 在虚拟结点的位置向量中找到最左偶的结点,左移一个位置即时该结点的逆向第一个虚拟结点,让其扩展势力范围到该虚拟结点,偶发现和原理不一样,无所谓,好懂好用就行。
(3) 删除该结点的势力范围数据。
5,附源码
经过简单功能测试,离实际应用还有距离。
/*************************************************************************
> File Name: consistenthash.cpp
> Author:zhangtx
> Mail: 18510665908@163.com
> Created Time: 2015年06月27日 星期六 14时32分42秒
************************************************************************/
#include<iostream>
#include<stdint.h>
#include <boost/unordered_map.hpp>
#include "StringAlgorithms.h"
#include "Common.h"
using namespace std;
using namespace boost;
string vnode="VNODE1:VNODE2:VNODE3:VNODE4:VNODE5:VNODE6:VNODE7:VNODE8:VNODE9:VNODE10:VNODE11:VNODE12:VNODE13:VNODE14:VNODE15";
string node="172.16.73.68:172.16.73.76:172.16.73.194";
const uint16_t NODE_COUNT=1024;
/*create node count virtual node*/
class ConsistentHash
{
enum STATUS{
EMPTY=0,
REAL,
EXT
};
struct Node
{
string m_nodeName;
uint64_t m_hashKey;
STATUS m_valid;
};
public:
ConsistentHash(uint16_t count);
~ConsistentHash(){
};
void printConfigInfo();
bool removeNode(string node);
bool addNode(vector<string> vnodes,string node);
string findNode(const string &key);
void releaseExtNodes(Node &node,string &vnodeName,size_t vCount);
void printcircle();
public:
bool init();
uint64_t hash(const string &str,uint64_t &crckey);
private:
Node *m_vNode;
/*虚拟结点到实际结点*/
unordered_map<uint64_t,string> m_v2eNode;
/*实际结点到虚拟节点位置*/
/*实际结点名称,虚拟节点名称,虚拟节点在环上的位置 */
unordered_map<string,unordered_map<string,vector<size_t> > > m_node2v;
uint16_t m_maxCount;
};
void ConsistentHash::printcircle()
{
cout<<endl<<"******************hash ring*****************"<<endl;
for(int idx=0;idx<m_maxCount;idx++)
{
cout<<idx<<" nodename="<<m_vNode[idx].m_nodeName<<" m_valid="<<m_vNode[idx].m_valid<<endl;
}
cout<<endl;
}
void ConsistentHash::printConfigInfo()
{
cout<<endl<<"consistent hash config**********************************"<<endl;
printcircle();
for(boost::unordered_map<string,unordered_map<string,vector<size_t> > >::iterator iter=m_node2v.begin();
iter!=m_node2v.end();iter++)
{
cout<<endl<<"************real node:"<<iter->first<<"****************"<<endl;
boost::unordered_map<string,vector<size_t> > &vNode2Pos=iter->second;
for(boost::unordered_map<string,vector<size_t> >::iterator iterV=vNode2Pos.begin();iterV!=vNode2Pos.end();iterV++)
{
cout<<endl<<"virtual node name:"<<iterV->first<<" count="<<iterV->second.size()<<endl;
for(size_t idx=0;idx<iterV->second.size();idx++)
cout<<iterV->second[idx]<<" ";
}
cout<<endl;
}
}
ConsistentHash::ConsistentHash(uint16_t count):m_maxCount(count)
{
}
string ConsistentHash::findNode(const string &key)
{
uint64_t crckey=Common::crc64(key.c_str(),key.length());
uint64_t pos=crckey%m_maxCount;
string result("");
if (m_vNode[pos].m_valid==EMPTY)
{
printf("does'not init ok.....'");
}
else
{
boost::unordered_map<uint64_t,string>::iterator iter=m_v2eNode.find(m_vNode[pos].m_hashKey);
if (iter!=m_v2eNode.end())
{
return iter->second;
}
else
{
printf("does't find real node'");
}
}
return result;
}
bool ConsistentHash::init()
{
if (m_maxCount<=0)
m_maxCount=NODE_COUNT;
m_vNode=new Node[m_maxCount];
for(size_t idx=0;idx<m_maxCount;idx++)
{
m_vNode[idx].m_valid=EMPTY;
}
vector<string> vnodes;
StringAlgorithms::Split(vnode,":",vnodes);
vector<string> nodes;
StringAlgorithms::Split(node,":",nodes);
size_t circleTimes=vnodes.size()/nodes.size();
size_t nodeIdx=0;
size_t adjustPos=0;
for(size_t idx=0;idx<vnodes.size();idx++)
{
uint64_t crckey=Common::crc64(vnodes[idx].c_str(),vnodes[idx].length());
/*将虚拟结点映射到环上*/
adjustPos=crckey%m_maxCount;
while(m_vNode[adjustPos].m_valid==REAL)
{
adjustPos=(adjustPos+1)%m_maxCount;
}
m_vNode[adjustPos].m_nodeName=vnodes[idx];
m_vNode[adjustPos].m_hashKey=crckey;
m_vNode[adjustPos].m_valid=REAL;
cout<<"nodename="<<vnodes[idx]<<" adjustPos="<<adjustPos<<endl;
/*
* 虚拟结点到实际结点的对应关系
* */
size_t realIdx=nodeIdx%nodes.size();
m_v2eNode[crckey]=nodes[realIdx];
unordered_map<string,unordered_map<string,vector<size_t> > >::iterator iter=m_node2v.find(nodes[realIdx]);
if (iter==m_node2v.end())
{
vector<size_t> vPosVec;
vPosVec.push_back(adjustPos);
unordered_map<string,vector<size_t> > vnode2Pos;
vnode2Pos[vnodes[idx]]=vPosVec;
m_node2v[nodes[realIdx]]=vnode2Pos;
}
else
{
unordered_map<string,vector<size_t> > &vnode2Pos=iter->second;
unordered_map<string,vector<size_t> >::iterator iterV2Pos=vnode2Pos.find(vnodes[idx]);
if (iterV2Pos!=vnode2Pos.end())
{
vector<size_t> &vPosVec=iterV2Pos->second;
vPosVec.push_back(adjustPos);
}
else
{
vector<size_t> vPosVec;
vPosVec.push_back(adjustPos);
vnode2Pos[vnodes[idx]]=vPosVec;
}
}
nodeIdx++;
}
/*扩展结点,到整个环上*/
for(uint16_t idx=0;idx<m_maxCount-vnodes.size();)
{
/*从任意结点开始*/
Node node=m_vNode[adjustPos];
/*结点对应的实际结点对应的所有虚拟结点对应的存储位置*/
boost::unordered_map<string,unordered_map<string,vector<size_t> > >::iterator iter=m_node2v.find(m_v2eNode[node.m_hashKey]);
if (iter==m_node2v.end())
return false;
/*虚拟结点到环上位置的映射*/
unordered_map<string,vector<size_t> >&v2Pos=iter->second;
unordered_map<string,vector<size_t> >::iterator iterV2Pos=v2Pos.find(node.m_nodeName);
{
vector<size_t> &tmpVec=iterV2Pos->second;
adjustPos=(1+adjustPos)%m_maxCount;
while(m_vNode[adjustPos].m_valid==EMPTY)
{
tmpVec.push_back(adjustPos);
m_vNode[adjustPos].m_valid=EXT;
m_vNode[adjustPos].m_nodeName=node.m_nodeName;
m_vNode[adjustPos].m_hashKey=node.m_hashKey;
adjustPos=(1+adjustPos)%m_maxCount;
idx++;
}
}
}
}
uint64_t ConsistentHash::hash(const string &str,uint64_t &crckey)
{
crckey=Common::crc64(str.c_str(),str.length());
return crckey%m_maxCount;
}
bool ConsistentHash::addNode(vector<string> vnodes,string nodeName)
{
unordered_map<string,vector<size_t> > v2PosMap;
for(size_t idx=0;idx<vnodes.size();idx++)
{
uint64_t crckey=0;
size_t adjustPos=hash(vnodes[idx],crckey);
/*调整pos到一个扩展节点上 */
while(m_vNode[adjustPos].m_valid==REAL)
{
adjustPos++;
}
Node node=m_vNode[adjustPos];
size_t coveredCount=0;
m_vNode[adjustPos].m_nodeName=vnodes[idx];
m_vNode[adjustPos].m_hashKey=crckey;
m_vNode[adjustPos].m_valid=REAL;
vector<size_t> posVec;
posVec.push_back(adjustPos);
coveredCount++;
adjustPos=(1+adjustPos)%m_maxCount;
while(node.m_hashKey==m_vNode[adjustPos].m_hashKey)
{
m_vNode[adjustPos].m_hashKey=crckey;
m_vNode[adjustPos].m_nodeName=vnodes[idx];
m_vNode[adjustPos].m_valid=EXT;
posVec.push_back(adjustPos);
adjustPos=(1+adjustPos)%m_maxCount;
coveredCount++;
}
m_v2eNode[crckey]=nodeName;
v2PosMap[vnodes[idx]]=posVec;
releaseExtNodes(node,vnodes[idx],coveredCount);
}
m_node2v[nodeName]=v2PosMap;
return true;
}
void ConsistentHash::releaseExtNodes(Node &node,string &vnodeName,size_t vCount)
{
unordered_map<uint64_t,string>::iterator iter= m_v2eNode.find(node.m_hashKey);
string realNode("");
if (iter!=m_v2eNode.end())
{
realNode=iter->second;
}
else
{
return;
}
unordered_map<string,unordered_map<string,vector<size_t> > >::iterator iterNodes=m_node2v.find(realNode);
if (iterNodes!=m_node2v.end())
{
unordered_map<string,vector<size_t> >::iterator iter=iterNodes->second.find(node.m_nodeName);
vector<size_t> &vpos=iter->second;
/*升序排序*/
std::sort(vpos.begin(),vpos.end());
vpos.resize(vpos.size()-vCount);
}
else
{
return;
}
}
bool ConsistentHash::removeNode(string node)
{
/*
* 找到删除节点对应的所有虚拟节点
* */
boost::unordered_map<string,unordered_map<string,vector<size_t> > >::iterator iter=m_node2v.find(node);
for(unordered_map<string,vector<size_t> >::iterator iterV2Pos=iter->second.begin();iterV2Pos!=iter->second.end();iterV2Pos++)
{
/*
* 循环处理各个虚拟节点
* */
//当前虚拟节点对应环上所位置
vector<size_t> &vpos=iterV2Pos->second;
// 对位置排序,
size_t vecLength=vpos.size();
size_t idx=0;
/*找上一个虚拟结点的位置*/
size_t lastPos=(vpos[0]-1)>0?vpos[0]-1:m_maxCount;
Node lastNode=m_vNode[lastPos];
{
/*
* 当前节点对应的实际节点名称
* */
Node curNode=m_vNode[vpos[idx]];
/*
* 取前一个节点的实际节点名称
* */
unordered_map<uint64_t,string>::iterator iterV2R= m_v2eNode.find(lastNode.m_hashKey);
string realNode("");
if (iterV2R!=m_v2eNode.end())
{
realNode=iterV2R->second;
}
else
{
continue;
}
/*
* 根据前一个节点的实际节点名称,找到其对应的所有虚拟节点
* */
unordered_map<string,unordered_map<string,vector<size_t> > >::iterator iterNodes=m_node2v.find(realNode);
unordered_map<string,vector<size_t> >&tVposMap=iterNodes->second;
/*
*在前一个节点的虚拟节点中找到前一个节点的虚拟节点数组
* */
unordered_map<string,vector<size_t> >::iterator iterVec=tVposMap.find(lastNode.m_nodeName);
vector<size_t> &tVpos=iterVec->second;
/*
* 将当前虚拟节点的第一个位置存储到前一个节点的位置数组,同时修改当前节点环中的信息
* */
tVpos.push_back(vpos[idx]);
m_vNode[vpos[idx]].m_hashKey=lastNode.m_hashKey;
m_vNode[vpos[idx]].m_valid=lastNode.m_valid;
m_vNode[vpos[idx]].m_nodeName=lastNode.m_nodeName;
idx++;
size_t nextPos=vpos[idx];
/*
* 循环前当前删除节点在环中的数据修改为前一个节点
* */
while(idx<vpos.size() && m_vNode[nextPos].m_hashKey==curNode.m_hashKey)
{
tVpos.push_back(nextPos);
m_vNode[nextPos].m_hashKey=lastNode.m_hashKey;
m_vNode[nextPos].m_valid=EXT;
m_vNode[nextPos].m_nodeName=lastNode.m_nodeName;
idx++;
nextPos=vpos[idx];
}
vector<size_t>().swap(vpos);
m_v2eNode.erase(curNode.m_hashKey);
}
}
m_node2v.erase(node);
return true;
}
int main(int argc,char *argv[])
{
ConsistentHash *ch=new ConsistentHash(100);
/*初始化环路*/
ch->init();
/*打印配置信息*/
ch->printConfigInfo();
vector<string> vnode;
vnode.push_back("vnode17");
vnode.push_back("vnode16");
ch->addNode(vnode,"172.16.73.55");
cout<<endl<<"print after add....................."<<endl;
ch->printConfigInfo();
ch->removeNode("172.16.73.55");
cout<<endl<<"print after remove....................."<<endl;
ch->printConfigInfo();
cout<<endl<<"print after remove 172.16.73.194....................."<<endl;
ch->removeNode("172.16.73.194");
ch->printConfigInfo();
cout<<endl<<"print after remove 172.16.73.68....................."<<endl;
ch->removeNode("172.16.73.68");
ch->printConfigInfo();
string key("123");
printf("%s",ch->findNode(key).c_str());
return 0;
}