一致性哈希
定义:
主要应用于解决分布式缓存的问题,在移除或者添加服务器(哪怕只改变一个)时,能够尽量的减少对已存在的服务请求与请求服务器之间的映射的影响。
在分布式系统中,通常可以将对象与节点的映射关系简单看做key%n,如果节点(即n)发生变动时,会使得很大一部分对象的映射找不到对应的“桶”,或者是新增的“桶”无法得到利用。而一致性哈希算法则具有极小的迁移成本,并且通过虚拟节点可以很大程度上实现负载的均衡性、分散性。
原理:
一致性哈希是将一段数据范围作为”桶“,再将实际具有的节点映射到”桶区域“中的某一个位置,在查找时,若查找到的是空桶,那么就朝设计时给定的方向(通常为正向)寻找第一个实际存在数据的桶。
缺点:
1、查找时若当前位置是空”桶“,则需要线性的继续查找。
2、数据量不多时,非空桶的分布可能不均衡。
为解决上述问题,就需要将一个数据量映射为多个虚拟位置。下面即为给予map和set实现的简单版的一致性哈希算法。实际验证中可以发现,当复制量处于一个较为合理的值时各物理节点的负载均是比较均衡的。
consistencyhash.h
#pragma once
#include<string>
#include<set>
#include<map>
#include<algorithm>
#include<iostream>
#include<iterator>
using namespace std;
class ConsistencyHash
{
public:
ConsistencyHash()
{
m_node.clear();
m_physical_node.clear();
}
ConsistencyHash(int multiple) : m_multiple(multiple)
{
m_node.clear();
m_physical_node.clear();
}
~ConsistencyHash() {};
string GetCurPhysical(string object);
bool AddPhysicalNode(string ip);
bool RemovePhysicalNode(string ip);
long FNVHash(string key);
bool DumpObjectNode(long object_min, long object_max);
void CoutAllNode();
int m_multiple = 1024; //虚拟节点复制倍数
map<long,string> m_node; //记录所有桶对应的哈希值与物理节点
set<string> m_physical_node; //记录当前物理节点
};
consistencyhash.cpp
#include "consistencyhash.h"
bool ConsistencyHash::AddPhysicalNode(string ip)
{
if (m_physical_node.find(ip) == m_physical_node.end())
{
m_physical_node.insert(ip);
for (int i = 0; i < m_multiple; ++i)
{
long hash = FNVHash(ip + "#" + to_string(i));
m_node.insert(make_pair(hash, ip));
}
cout << "add sucessfully!" << endl;
return true;
}
return false;
}
// 32位的 Fowler-Noll-Vo 哈希算法
// https://en.wikipedia.org/wiki/Fowler–Noll–Vo_hash_function
long ConsistencyHash::FNVHash(string key) {
int p = 16777619;
long hash = 2166136261;
for (int idx = 0, num = key.length(); idx < num; ++idx) {
hash = (hash ^ key[idx]) * p;
}
hash += hash << 13;
hash ^= hash >> 7;
hash += hash << 3;
hash ^= hash >> 17;
hash += hash << 5;
if (hash < 0) {
hash = abs(hash);
}
return hash;
}
bool ConsistencyHash::RemovePhysicalNode(string ip)
{
if (m_physical_node.find(ip) != m_physical_node.end())
{
m_physical_node.erase(ip);
for (int i = 0; i < m_multiple; ++i)
{
long hash = FNVHash(ip + "#" + to_string(i));
m_node.erase(hash);
}
cout << "remove all sucessfully!" << endl;
return true;
}
cout << "no such ip!" << endl;
return false;
}
string ConsistencyHash::GetCurPhysical(string object)
{
if (m_physical_node.size() <= 0)
{
cout << "no physical node now!" << endl;
return to_string(-1);
}
long hash = FNVHash(object);
map<long, string>::iterator it = m_node.lower_bound(hash);
if (it == m_node.end())
it = m_node.begin();
return it->second;
}
bool ConsistencyHash::DumpObjectNode(long object_min, long object_max)
{
if (object_min > object_max) return false;
map<string, long> node_sys;
for (auto s : m_physical_node)
{
node_sys.insert(make_pair(s, 0));
}
for (long i = object_min; i < object_max; ++i)
{
long hash = FNVHash(to_string(i));
map<long, string>::iterator it = m_node.lower_bound(hash);
if (it == m_node.end())
it = m_node.begin();
cout << it->first << endl;
node_sys[it->second] = node_sys[it->second] + 1;
}
for (map<string,long>::iterator it = node_sys.begin();it!=node_sys.end();it++)
{
cout << it->first<<":"<< it->second << " : " << (it->second / static_cast<double>(object_max - object_min))*100 <<"%"<<endl;
}
return true;
}
void ConsistencyHash::CoutAllNode()
{
for (map<long, string>::iterator it = m_node.begin(); it != m_node.end(); it++)
{
cout <<"hash_value: "<<it->first << "ip_address: " << it->second << endl;
}
}
main.cpp
#include "consistencyhash.h"
int main()
{
ConsistencyHash m_hash(100);
m_hash.AddPhysicalNode("192.168.15.1");
m_hash.AddPhysicalNode("192.168.15.2");
m_hash.AddPhysicalNode("192.168.15.3");
m_hash.AddPhysicalNode("192.168.15.4");
m_hash.AddPhysicalNode("192.168.15.5");
m_hash.CoutAllNode();
m_hash.DumpObjectNode(1, 1000);
return 0;
}
参考:https://blog.csdn.net/kefengwang/article/details/81628977
(java版本实现)