随笔系列之 一致性哈希(consistent hash)

简单说说一致性hash

关于hash

首先,什么是hash以及为什么要用hash?简单带过:
hash本身就是将目标对象或者数据片段映射到另外一个数据片段中.在编程环境下,通常意味着将一个数据或数值数据或字符串数据等等,映射为一个整型数值. 那么为何要用hash?很简单的一个原因:为了速度!为了方便!当然,这里指的是日常编程需要对hash的用法.除了方面访问数据,hash还有别的用途,比如在安全领域,可能需要对比如密码这样的重要数据进行加密,也有一些如md5,sha1这样的hash算法的应用,当然,此时的hash目标就不相同了!有着安全性如反破解等等考量.

  • 简单hash的应用场景:

    比如,我们有一组数据222,333,11,22,….,当我们需要经常查询这些数据的时候,比如查询这个数据是否存在,我们可以将他们存入一个list列表当中,但是这样的话进行查询时就需要按顺序查询,查询时间为O(N);但是,如果我们可以将他们看做是索引,那么就可以直接访问这个索引,查看数据是否存在等等.但是,就此而言,我们的数据可能是32位int或者64位int,如果直接作为索引看待,直接导致的问题就是内存消耗太大,并且空间利用率极低; 因此,我们可以考虑根据数据的数量来进行映射,比如数据数量为N,那么存储时可以根据hash(num) mod N 来进行存储.其中num为其中的数据.
    以上,很简单,不再赘述.

  • 常见的hash使用,诸如各种高级编程语言实现的hash set,主要用于将数据存入一个集合set中,之后可以以O(1)的访问时间查看数据是否存在! 对于hash map或者hash table,则是将目标数据按照唯一的键进行存储,其值可以是任意的,然后可以根据key进行O(1)的快速访问.

好的,一致性hash!(分布式hash)


  • 什么是一致性hash?

一致性哈希,主要应用于分布式领域.想象一下,如果我们有若干服务器,假设3个;然后我们有若干数据要存储,假设8个,那么我们可能使用最简单的方式进行存储,比如直接按照服务器的数量取模,然后分别存储各个数据到相应的服务器上.
我们使用分布式hash的目的是,为了加快数据访问速度,提升系统的性能.

首先:如果不适用分布式缓存,那么我可能需要直接从数据源读取数据,比如直接从数据库读取,一方面来说速度很慢,另一方面来说,在web环境下的巨大访问量,数据库可能会承受不住甚至crash.因此,分布式缓存的目的就是,一方面提升性能,另一方面缓解压力.客户端请求数据,首先会查询缓存,如果缓存中没有,我们称之为key miss,那么就需要从数据源读取数据,然后将其根据查询key缓存到缓存中.以便下次访问可以直接返回数据.

举例:

  • 3个服务器A(0),B(1),C(2) 数据1,2,3,4,5,6,7,8;如何存储?

数据 对3取模 服务器
1 1 B
2 2 C
3 0 A
4 1 B
5 2 C
6 0 A
7 1 B
8 2 C

好的,以上没啥问题!但是!万一要是其中一个服务器挂了呢?或者新增加了新的机器呢?OK,看看如果B挂了,会咋样?
A(0),C(1)

数据 对2取模 服务器
1 1 C
2 0 A
3 1 C
4 0 A
5 1 C
6 0 A
7 1 C
8 0 A

我们发现,除了B的数据要发生迁移之外,其他服务器上的数据也要相应的迁移,由此带来了巨大的不便性!
一旦发生如上情况,就会出现大量的key miss问题,进而导致大量的数据源访问,并由此可能导致数据源服务器负载过量而crash.

Here comes consistent hash

于是乎,一致性哈希应运而生!
好的,开始想象如何解决以上的问题?也就是如何创建一致性hash算法.

  • 在考虑一致性hash时,我们首先考虑将原始目标数据对象hash值映射到一个圆上,或者一个环上.
  • 首先,假设我们的数据最大值为10.
  • 然后,比如,对数据2,我们降到映射到360度的环上,它的度为2/10 * 360=72°;
  • 接着,将我们的服务器也映射到这个环上,通常可以根据服务器的ip地址等进行hash映射,将他的hash值映射到这个环上,通常来说服务器的hash映射方法与数据的hash映射应当保持一致,这样可以使得数据更好的均匀分布到各个server上.这里,依旧可以假设,服务器的hash值依旧为0,1,2,那么据此,我们可以得到新的环映射表.

这里,对于数据和server映射的描绘用环图更合适,但是,为了偷懒,就用表吧 –)

数据或者服务器 hash值 映射到环上angle
1 1 36°
2 2 72°
3 3 108°
4 4 144°
5 5 180°
6 6 216°
7 7 252°
8 8 288°
A 0
B 1 36°
C 2 72°

* 实际上,server的hash值应该更均衡些,这样与数据之间才会更加均匀,emmm…先这样吧,,,

  • 然后,我们就可以根据数据和服务器在环上的位置,按照增序的方式将数据放到server上,也就是每个数据顺时针查看server,并将其放置于第一个遇到的server上!
  • 在实际编程实现中,我们可以将server放到一个list中,并从小到大排好序,然后对每一个数据,根据的他的度,查找list,直到遇到第一个比他大的,that’s it!如果,没有,那就循环放回第一个服务器节点!
  • OK,Sounds easy enough!

虚拟节点问题

那么问题来了,很明显我们上面的三个服务器在这个环上很不均衡,因此我们通常会构造一些虚拟节点,到这个环上!比如每个节点构造10个,A0,A1,–A9,…B0—B9,…C0,..C9 ,然后也计算他们的hash值,并放置于环,然后对于数据查询存放位置为AX 的都放到服务器A上,以此类推. 关于虚拟节点的构造问题,还有一些技巧,据了解,对于5-10个server的,10个虚拟节点很好,对于50-100个server,可能需要100–500个…

Why? (一致性Hash的优点)

上面只说了一致性哈希的实现方案.但是还没说他为何这么实现呢?

好的,简答的分布式hash会带来因server crash或者新增server的数据重新调整存储位置的高昂代价.如果使用这个环形映射,当有服务器挂掉时,我们会发现,只有那个服务器上的数据会被分配到其他服务器的虚拟节点上(这也是虚拟节点的好处!试想,如果没有虚拟节点,那么某个节点服务器挂掉,其上所有数据都会被移送到下一个正常的服务器节点上,势必会让下一个服务器负载过重甚至crash!);当加入新的server时,也只会在加入的server的虚拟节点与其之前的虚拟节点之间发生数据迁移; 总的来说数据迁移量都在N/K的范围.[其中N为数据总量,K为服务器数量,或为初始服务器数,或为crash/新增后服务器数.]

你可能感兴趣的:(随笔系列,一致性哈希,consistent,hash,hash)