随着数据规模越来越大,光靠一个机器是无法承载这些数据的。因为业内常见的做法是将数据进行分片,存储在各个机器上面。 由于数据存储在集群的多台服务器上面,就需要一个有效的办法快速找到某个数据的存储位置,这就是数据路由。这节主要介绍数据分片以及数据路由的一些算法。
目前的分片算法大体可以分为两类
- 哈希分片
- 范围分片
数据路由算法一般和分片算法绑定在一块
一、哈希分片
通过哈希分片是进行数据分片的常见手段,目前最常见的分片算法分别是
- Round Robin
- 虚拟桶
- 一致性hash
Round Robin
这个算法很简单,分片就是根据一个hash函数对数据进行hash,然后取模当前机器的数量即可。
公式如下:
# N 表示机器的数量
# host 表示数据要存储的目标机器
host = hash(data) mod N
假设现在有3台机器,编号0、1、2。这时候来了一个数据data,经过hash算法得到的值是99,即hash(data)=99
,然后 99 mod 3 = 0 。我们就把这个数据放到0号机器上。
同样的,数据路由也可以直接根据该算法去找到对应的机器。
缺点:
这种算法有个缺点,就是不灵活。当机器数量发生变动时,需要重新分配所有的数据。我们设想一下,假设原来是3台机器存储数据,这时候又新增了一台,原先的数据再按前面的公式去路由就找不到了,所以这是我们就需要对所有的旧数据进行重新分片。如果数据量很大,这就是一个大工程了。
因此,Round Robin对于有横向扩展需求的场景来说很不友好。
虚拟桶
虚拟桶是MemBase提出的一个数据分片的算法,主要思路是先将数据进行哈希映射到某些虚拟桶上,然后再维护一份虚拟桶和机器之间的映射关系。
上图来自《大数据日知录》
从上面的虚拟桶的图中,我们可以看出,数据通过hash的方式映射到某个桶,之后查找表桶和服务器的映射关系表找到对应服务器。这里数据和虚拟桶是多对一的关系,虚拟桶和服务器也是多对一的关系。
虚拟桶的数据路由也是先哈希再查找表。
这个算法比Round Robin灵活,当添加一个机器时,不会影响数据路由。当然,为了数据分布均衡,我们可以把一些虚拟桶迁移到新的服务器上面。即使是这样,我们也只需要迁移一部分数据即可。
缺点:
需要维护一个虚拟桶到服务器映射的表,稍微麻烦些。
一致性hash
一致性哈希算法在1997年由麻省理工学院的Karger等人在解决分布式Cache中提出的,设计目标是为了解决因特网中的热点(Hot spot)问题。
首先我们可以先定义一个哈希空间,取值范围在0~31,也就是2的5次方。我们可以把这个哈希空间想象成一个环,0是环首,31是环尾,0和31相连。
现在假设有5个节点,我们对它们进行哈希,让他们落到这个哈希空间的各个点上,如下图:
这些节点形成了一个有环的链表,每个节点负责一部分区间的数据。
- N14节点负责6-14区间的数据
- N20节点负责15-20区间的数据
- N25节点负责21-25区间的数据
- N29节点负责26-29区间的数据
- N5节点负责29-31以及0-5区间的数据
因此假设现在有个数据,算出它的hash值是28,则它应该落入N29的节点上。
这种分片算法的数据路由需要一些技巧,一种简单的路由算法是从N5节点开始,沿着节点链表一路遍历下来,判断数据哈希的结果是否落在该节点负责的区间。这种路由算法的时间复杂度是O(n),性能相对较低。因此,我们可以使用另外一种数据路由算法。
一致性hash路由算法
《大数据日知录》中记录了一种算法,主要是每个节点维护一张距离表,主要记录当前节点离距离1、2、4、8、16…的数值是哪个节点负责的。之后当该节点需要去路由一个数据的时候,先判断该数据是否属于自己或者属于它的后继节点,如果不是,则根据距离表找到离该数据最相近的一个节点,然后把路由的事情交给这个节点,之后这个节点也执行相同的算法,直到找到负责该数据的节点为止。
个人感觉上面那个算法并不算最优解,我们可以把问题抽象成数列查找某个值的问题。我们可以将各个节点按数字排序,每个节点都维护排序号的这个数列,然后要查找某个值时,根据二分法查找到小于等于这个值的节点即可。
节点的新增和离开
一致性hash可以很好的解决常规哈希分片中不灵活的问题,我们假设现在在N5和N14之间加了一个节点N8,我们不用对所有的数据进行重新分配,只需要将原先N14上区间6-8的数据迁移到N8上面即可,数据迁移成本大大降低。
另外,有节点加入时还涉及到一些稳定性检测算法,以维护节点之间的前驱和后继的关系。这里就不具体介绍这个算法,感兴趣的读者可以阅读《大数据日知录》查阅。
相反,如果有节点离开,就将这个节点上面的数据迁移到对应的节点上。通常,数据下线可能还要考虑异常关闭的情况,这时我们可以通过做数据备份来保护这些数据在节点异常下线后仍然可以使用。
一致性hash的一些优化思路
从上面的例子我们可以看出,数据在各个节点分布的不是很均匀,有些节点负责的数据区间比较大,有些比较小,如果刚好性能好的机器分配到的数据区间就很小,但是性能差的机器分配到的数据区间又比较大,就极大的浪费了机器资源。因此,我们可以利用虚拟节点来增加数据落到性能好的机器上的比率,同时也可以通过虚拟节点让数据分布更均衡,避免数据倾斜问题。比如性能好的可以多设置几个虚拟节点,这些虚拟节点实际都代表同一个节点。
二、范围分片
范围分片主要是对数据主键进行排序,然后切分成一个个分片,在存储到各个节点的算法。一般存储系统会维护一个分片和机器的映射表,这个表记录了每个分片的最小主键值对应哪个节点,之后通过二分法我们就可以快速路由数据在哪个分片以及对于的节点。
因为范围分片要不断的执行排序,因此一般适用LSM树来写入数据,LSM树是一种高效写入的索引结构。在Hbase中就用到了LSM树,当然,Hbase的分配也是基于范围分片。
参考资料
《大数据日知录》