一致性哈希(consistent hashing)

来源:互联网 发布:淘宝网健身手球 编辑:程序博客网 时间:2024/05/17 20:33

啥是consistent hashing?

不管是一致性哈希还是二致性哈希还是什么阿猫阿狗哈希,都是哈希。只不过它有些特殊,特殊性就体现在名字上一致性
为了表明它的与众不同,先看看啥是哈希(hashing)。在英文中,字面上hash大致是切分的意思。hashing通常是将一个数据通过哈希函数映射成另一种数据的过成。例如将字符串hello映射成57,Hasta la vista, baby映射成33。具体映射逻辑就是哈希函数的逻辑,这个由自己来定。

哈希表

当用一个数据存储一群人的名字时候,为了增加事后查找效率,通常使用名字的hash值作为数组索引,即名字作为key,用某种hash function算出hash值。比如名字为David的名字经过hash后的值为4,那么存放在数组的索引为4的位置。
当人名多了,hash值并不能保证唯一,如果David和Andson的hash值都是4,那么就造成了hash碰撞。通常的解决方案是数组索引为4的位置不再存放一个元素了,取而代之的是一个链表,链表存储David和Andson,如果再有其它名字也hash到了4这个位置,那么将他存在该链表的末尾。
这样的数据结构就是hash table

在分布式环境中应用hash

想想一下,有几台分布式的Memcached服务器,一些数据对象通过hash 映射,均匀分布在这几台服务器上存储。如果一台服务器挂了,或者添加服务器,那么hash function里的映射逻辑就要跟着变,读取数据的时候,被分配到其他服务器上去读取,自然读取不到,然后Memcached去原始服务器上读取数据。这样的情况在数据量非常大的情况下,会造成原始服务器压力陡增,进而崩掉。
怎样保证在缓存服务器减少或者增加的情况下,尽量少地影响原数据的读取位置呢?

一个解决方法就是 一致性哈希。

一致性哈希

一致性哈希的整体思想就是将数据和服务器通过相同的hash function映射到一个单元环上(unit circle)。每个数据存放在逆时针方向上最近的那个服务器上。

这里写图片描述

图中,A B C 是三台服务器,其他圆圈是数据。按逆时针方向查找,每个数据都有自已的“归属”。
这里写图片描述

如果此时,服务器B挂了,B存储的数据“bill”被rehashing道服务器C上,而其他数据几乎不受影响。

下面几十行golang代码模拟了模拟了一致性hash的unit circle。

package mainimport "fmt"import "sync"import "hash/crc32"import "sort"import "errors"// unit circletype Ring struct {    Nodes Nodes // 环上部署的服务器节点    sync.Mutex}// 用数组表示一个循环链表,表达unit circletype Nodes []*Nodefunc (n Nodes) Len() int {return len(n)}func (n Nodes) Swap(i, j int) {n[i], n[j] = n[j], n[i]}func (n Nodes) Less(i, j int) bool {return n[i].HashId < n[j].HashId}type Node struct {    Id string    HashId int  }func NewNode(id string) *Node {    return &Node{        Id: id,        HashId: hashId(id),    }}// 这里使用crc32.ChecksumIEEE方法取得节点的hash值,可以使用其他自定义func hashId(key string) int {    return int(crc32.ChecksumIEEE([]byte(key)))}func NewRing() *Ring {    return &Ring{        Nodes: Nodes{},    }}func (r *Ring) AddNode(id string) {    r.Lock()    defer r.Unlock()    node := NewNode(id)    r.Nodes = append(r.Nodes, node)    sort.Sort(r.Nodes)}func (r *Ring) RemoveNode(id string) error {    r.Lock()    defer r.Unlock()    i := r.Search(id)    if i >= len(r.Nodes) || id != r.Nodes[i].Id {        return NotFountError    }    r.Nodes = append(r.Nodes[:i], r.Nodes[i+1:]...)    return nil}func (r *Ring) Search(id string) int {    searchfn := func (i int) bool {        return r.Nodes[i].HashId >= hashId(id)    }    return sort.Search(len(r.Nodes), searchfn)}func (r *Ring) Get(id string) string {    i := r.Search(id)    if i >= len(r.Nodes) {        i = 0    }    return r.Nodes[i].Id}var NotFountError = errors.New("node not found")func main() {    r := NewRing()    r.AddNode("jian")    r.AddNode("yong")    r.AddNode("li")    fmt.Println(r.Get("sf"))    r.RemoveNode("li")    fmt.Println(r.Get("sf"))    fmt.Println(r.RemoveNode("yon"))}
原创粉丝点击