一致性哈希算法(consistent hash)的黑科技
来源:互联网 发布:阿里云服务器增加磁盘 编辑:程序博客网 时间:2024/05/05 22:24
一致性哈希算法(consistent hash)的黑科技
这是一个来自Google的零内存消耗、均匀、快速、简洁的一致性哈希算法 – Jump Consistent Hash
算法的作者是Google的John Lamping和Eric Veach,论文原文链接 – 点这里,一些讨论 – 点这里
整篇文章基于对论文原文的翻译,掺杂自己的一些思想理解,以及对该算法代码的适应场景分析修改,水平有限,难免有偏差:D
一致性哈希(Jump Consistent Hash)算法设计目标
平衡性,把对象均匀地分布在所有桶中。
单调性,当桶的数量变化时,只需要把一些对象从旧桶移动到新桶,不需要做其它移动。
简单理解就是:m个对象,n个桶,每个桶分到的对象应该在m/n个左右,误差不应太大;在桶增加或者桶减少1时,影响到的对象应该也在m/n个左右,其余对象不受影响。
满足这些目标,Jump Consistent Hash的总体设计思路是:计算当bucket数量变化时,有哪些输出需要变化。
直接上代码
int32_t ch(int64_t key, int32_t num_buckets)
{
srand
(key);
int32_t b = -1;
int32_t j = 0;
while
(j < num_buckets) {
b = j;
double
r =
rand
() / (
double
)RAND_MAX;
j =
floor
((b + 1) / r);
}
return
b;
}
测试用例与输出
int
main()
{
printf
(
"======== bucket 4 =========\n"
);
for
(
size_t
i = 20000; i < 20020; i++) {
printf
(
"i:%d b:%lu\n"
, i, ch(i, 4));
}
printf
(
"======== bucket 3 =========\n"
);
for
(
size_t
i = 20000; i < 20020; i++) {
printf
(
"i:%d b:%lu\n"
, i, ch(i, 3));
}
printf
(
"======== bucket 2 =========\n"
);
for
(
size_t
i = 20000; i < 20020; i++) {
printf
(
"i:%d b:%lu\n"
, i, ch(i, 2));
}
return
0;
}
======= bucket 4 ======= ======= bucket 3 ======= ====== bucket 2 ======
i:20000 b:0 i:20000 b:0 i:20000 b:0
i:20001 b:3 i:20001 b:2 i:20001 b:0
i:20002 b:3 i:20002 b:0 i:20002 b:0
i:20003 b:0 i:20003 b:0 i:20003 b:0
i:20004 b:0 i:20004 b:0 i:20004 b:0
i:20005 b:2 i:20005 b:2 i:20005 b:0
i:20006 b:0 i:20006 b:0 i:20006 b:0
i:20007 b:2 i:20007 b:2 i:20007 b:1
i:20008 b:3 i:20008 b:2 i:20008 b:1
i:20009 b:1 i:20009 b:1 i:20009 b:1
i:20010 b:0 i:20010 b:0 i:20010 b:0
i:20011 b:0 i:20011 b:0 i:20011 b:0
i:20012 b:2 i:20012 b:2 i:20012 b:1
i:20013 b:0 i:20013 b:0 i:20013 b:0
i:20014 b:0 i:20014 b:0 i:20014 b:0
i:20015 b:2 i:20015 b:2 i:20015 b:0
i:20016 b:3 i:20016 b:0 i:20016 b:0
i:20017 b:3 i:20017 b:1 i:20017 b:1
i:20018 b:3 i:20018 b:2 i:20018 b:1
i:20019 b:2 i:20018 b:2 i:20019 b:0
从输出结果来看, 满足了平衡与单调性(这里取得对象数量比较有限,更多的数量会分布更加均匀)。
一致性哈希(Jump Consistent Hash)算法分析
原论文中有大量的概率论证该算法满足平衡与单调性,这里直接忽略,我会撇开概率相关的东西说说我对它直观的理解。
代码的核心部分,srand(key)与rand()。这里一个使用线性同余方法产生伪随机数的接口。他的特点是,一旦key确定,那么在这个接口调用里使用rand()产生的随机数必然是相同的。关于线性同余的原理晚上很多介绍,这里测试验证代码:
int32_t test_rand(int64_t key)
{
srand
(key);
printf
(
"1->key:%llu rand:%d\n"
,key,
rand
());
printf
(
"2->key:%llu rand:%d\n"
,key,
rand
());
}
int
main()
{
test_rand(1);
test_rand(2);
test_rand(3);
test_rand(3);
test_rand(2);
test_rand(1);
return
0;
}
1->key:1
rand
:1804289383
2->key:1
rand
:846930886
1->key:2
rand
:1505335290
2->key:2
rand
:1738766719
1->key:3
rand
:1205554746
2->key:3
rand
:483147985
1->key:3
rand
:1205554746
2->key:3
rand
:483147985
1->key:2
rand
:1505335290
2->key:2
rand
:1738766719
1->key:1
rand
:1804289383
2->key:1
rand
:846930886
可以若key一旦确定,那么rand()的伪随机值就是按顺序确定的。这样就保证了,同一个key每次调用ch()后会得到相同的桶索引,除非这个桶索引不在(上例中bucket 4->bucket 3,在bucket 4中b:3的对象会被重新分配)
适用场景分析
比如说我们对于hash桶,存在这样的映射:
0->机器A,1->机器B,2->机器C,3->机器D
当3->机器D从映射表消失后,根据ch()的算法,原来分配到b:3桶的会被重新分配到bucket 0-2。那么问题来了, 若是1->机器B从映射表消失呢?
根据ch()算法,返回还是会重新分配bucket 0-2。那么现在机器映射0,2,3怎么和现在的bucket 0-2对应。外部要维护动态的映射表,实时更新……想想都……
一些小修改
//为测试用例而生的结构体
struct
test {
int32_t num;
bool
active;
};
int32_t cch(int64_t key, vector<
struct
test> &v)
{
srand
(key);
next:
int32_t b = -1;
uint32_t j = 0;
while
(j < v.size()) {
b = j;
double
r =
rand
() / (
double
)RAND_MAX;
j =
floor
((b + 1) / r);
}
if
(v[b].active) {
return
v[b].num;
}
else
{
goto
next;
}
}
active是需要算法外部维护的,标记这个桶是否还在有效状态,有效-true,无效-false。
测试与输出
int
main()
{
vector<
struct
test> v;
for
(
size_t
i = 0 ; i < 4; i++) {
struct
test t;
t.num = i;
t.active =
true
;
v.push_back(t);
}
printf
(
"======== bucket 0 1 2 3 =========\n\n"
);
for
(
size_t
i = 20000; i < 20020; i++) {
printf
(
"i:%d b:%lu\n"
, i, cch(i, v));
}
v[0].active =
false
;
printf
(
"======== bucket 1 2 3 =========\n\n"
);
for
(
size_t
i = 20000; i < 20020; i++) {
printf
(
"i:%d b:%lu\n"
, i, cch(i, v));
}
v[1].active =
false
;
printf
(
"======== bucket 2 3 =========\n\n"
);
for
(
size_t
i = 20000; i < 20020; i++) {
printf
(
"i:%d b:%lu\n"
, i, cch(i, v));
}
printf
(
"======== bucket 2 3 8 =========\n\n"
);
struct
test t;
t.num = 8;
t.active =
true
;
v.push_back(t);
for
(
size_t
i = 20000; i < 20020; i++) {
printf
(
"i:%d b:%lu\n"
, i, cch(i, v));
}
return
0;
}
===bucket 0 1 2 3=== ===bucket 1 2 3=== ===bucket 2 3=== ===bucket 2 3 8===
i:20000 b:0 i:20000 b:3 i:20000 b:3 i:20000 b:3
i:20001 b:3 i:20001 b:3 i:20001 b:3 i:20001 b:3
i:20002 b:3 i:20002 b:3 i:20002 b:3 i:20002 b:8
i:20003 b:0 i:20003 b:2 i:20003 b:2 i:20003 b:2
i:20004 b:0 i:20004 b:2 i:20004 b:2 i:20004 b:2
i:20005 b:2 i:20005 b:2 i:20005 b:2 i:20005 b:2
i:20006 b:0 i:20006 b:2 i:20006 b:2 i:20006 b:8
i:20007 b:2 i:20007 b:2 i:20007 b:2 i:20007 b:2
i:20008 b:3 i:20008 b:3 i:20008 b:3 i:20008 b:3
i:20009 b:1 i:20009 b:1 i:20009 b:3 i:20009 b:3
i:20010 b:0 i:20010 b:2 i:20010 b:2 i:20010 b:2
i:20011 b:0 i:20011 b:3 i:20011 b:3 i:20011 b:3
i:20012 b:2 i:20012 b:2 i:20012 b:2 i:20012 b:2
i:20013 b:0 i:20013 b:1 i:20013 b:3 i:20013 b:8
i:20014 b:0 i:20014 b:2 i:20014 b:2 i:20014 b:8
i:20015 b:2 i:20015 b:2 i:20015 b:2 i:20015 b:2
i:20016 b:3 i:20016 b:3 i:20016 b:3 i:20016 b:3
i:20017 b:3 i:20017 b:3 i:20017 b:3 i:20017 b:3
i:20018 b:3 i:20018 b:3 i:20018 b:3 i:20018 b:3
i:20019 b:2 i:20019 b:2 i:20019 b:2 i:20019 b:2
如此一来解决bucket_num与机器唯一映射的问题。付出的代价是:1、在有桶状态变更时,受影响对象重新寻桶的循环次数大约增加0.8倍。2、在桶状态时平衡性会略缺失,这点我没有科学的用概率论证,但是从测试用例结果来看是如此。
- 一致性哈希算法(consistent hash)的黑科技
- 【算法学习】Consistent Hash 一致性哈希
- 一致性哈希(consistent hash)
- 一致性哈希(consistent hash)
- 一致性Hash算法(Consistent Hash)
- 一致性哈希(Consistent Hash)
- Consistent Hash 一致性哈希
- 算法系列-一致性哈希算法consistent hash
- Memcache的一致性 Hash 算法(Consistent hashing)
- Consistent-Hash 一致性哈希算法 分布式存储
- 一致性hash算法(consistent hashing)
- 一致性 hash 算法( consistent hashing )
- 一致性 hash 算法( consistent hashing )
- 一致性 hash 算法( consistent hashing )
- 一致性 hash 算法( consistent hashing )
- 转:一致性 hash 算法( consistent hashing )
- 一致性 hash 算法( consistent hashing )
- 一致性 hash 算法( consistent hashing )
- Android Service完全解析,关于服务你所需知道的一切(上)
- 原生(DBUtils)事务与回滚
- CEF3:与 JavaScript 整合(一)
- Mac 安装 nodejs (图文详细步骤)及SublimeText运行JavaScript控制台
- 最大子阵和(DP)&& Hdu 1003 && Hdu 1081
- 一致性哈希算法(consistent hash)的黑科技
- Android弹出popupWindow 背景变暗(背景半透明)(两种写法)
- 老司机带你学系列-Hibernate入门到精通-1
- 记录下关于安卓项目集成微信支付的过程
- JavaWeb学习总结——文件的上传和下载
- echarts常用设置
- eclipse中创建带有父子结构的项目
- EventBus3.0使用
- BigInteger类