一种一致性HASH算法的实现方法,附核心代码

一致性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_validREAL

存储虚拟结点的HASH值与实际结点的对应关系,对应关系有多种算法,这里应用的是简单的也是最均衡的顺序对应算法,比如有10个虚拟结点,2个实际结点,第1,3,5,7,9虚拟结点就对应第1个实际结点,第24680虚拟结点就对应第2个实际结点,

虚拟结点到实际结点的映射关系存储在m_v2eNode

(3)所有的虚拟结点顺时针扩展势力范围直到遇到下一个虚拟结点。

扩展的虚拟结点m_validEXT,其它都一样。

    4)记录第个实际结点对应那些虚拟结点,每个虚拟结点又在m_vNode中什么位置,主要是做增加和删除的时候会用到。存储数据在m_node2v

2,         查询

(1)用户输入key,转为一个HASH值,做为目的结点在m_vNode中的下标,从目的结点中取出hash值,查询m_vNode,即可得到实际结点,时间复杂度仅为o1)。

3,         添加结点

(1) 输入为实际结点的名称,和其对应的一系列的虚拟结点名称。

(2) 映射虚拟结点到m_vNode

映射的位置如果刚好有一个结点,并且m_validREAL,则要顺时针顺延到一个状态为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;
}


 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值