数据复制是通过冗余副本的方式,提高数据的高可用,数据复制考虑的是从节点的状态要跟上主节点,以及多主节点之间的数据冲突问题。但是数据复制始终受限于单节点的容量问题,比如一个节点最多能放PB、TB级的数据量,但它总有一个上限。
所以数据分区把数据切分成不同的区间,分别放在不同的节点,只要分区节点足够多,就能容纳足够多的数据。在数据分区时,我们是希望各个分区的数据能均衡分布,而不是某些分区节点成为热点而造成压力。
数据分区有几个方面需要关注:
(1) 如何把数据切分;
(2) 切分后,对于分区节点的伸缩,数据又如何再平衡
(3) 数据如何路由到目标分区节点
一、如何分区
1、基于区间分区
{
"name": "Tom",\
“favorite": “reading"
}
{
"name": “Alex",
“favorite": “dancing"
}
复制代码
考虑以上文档,我们对关键字name进行分区,假如把首字母A-F放到分区1,H-N放到分区2,以此类推,把剩余的区间划分到其他分区。
这种分区简单,且对分区内,还能进行范围查询,但是也很容易造成数据分布不均衡,比如大部分数据落在A-F的分区1,且其他分区只有一小部分数据。
2、基于哈希分区
假设分区1的哈希值范围[0,10000],分区2的哈希值范围是[10001, 20000],以此类推,根据某一哈希函数得到:
hash(“Tom”) = 10210, hash(“Alex”) = 98
基于哈希函数分区,可以得到较为均衡的分布,但是在分区内无法进行范围查询。
3、复合分区
复合分区其实是区间分区和哈希分区的一种折中,使用多个列组成复合键,复合键的一部分用来哈希分区,其他部分用来范围查询。
二、分区再平衡
我们一般不会使用取模的方式来划分分区,因为在增加分区节点时,会频繁地有数据从一个分区转移到另外一个分区,比如原来的 %10 和增加一个分区节点后的 %11,结果很不一样,就需要迁移。
我们的目标就是为了在增加分区节点时,尽量减少迁移。有两种措施:1是采用固定数量的分区,2是动态分区。
1、固定数量的分区
首先创建远超节点数的分区数,为每个节点分配多个分区,如果集群增加节点,那么就可以从现有的每个节点分走几个分区,这样直到数据再次均衡。
这个过程会有数据从部分节点迁移到新的节点,但是总体上,分区数是不会变的,因为分区数是我们在分区之前对系统未来发展最大限度的一个预估,足以支撑数据容量。但是这个预估有点难度,分区数多了和少了,都会造成不必要的开销,很难预估到合适的数量。
2、动态分区
动态分区也是每个节点承载多个分区,但是分区的数量是由数据量动态划分的,动态分区还会把一个大的分区拆开变成多个分区,也可能会把小的分区合并成一个分区。
动态分区的分区到节点的映射,可以由手工执行,也可以软件自动执行,或者二者结合起来。
三、数据路由
我们现在考虑的是,如何能把数据路由到对应的?
1、由客户端请求某个节点,如果数据在该节点,就由该节点返回,如果不存在,那就把请求转发到其他节点,最终返回给客户端。
2、客户端请求路由节点,由路由节点转发到对应的分区节点,路由节点不处理业务逻辑。
3、客户端持有分区-节点的映射关系,由客户端直接访问节点。
但是还有一个问题是,分区和节点的映射关系,如何维护,我们要知道,分区和节点都是会变化的,所以,这种关系是要时刻维护,并把更新通知到上述的参与者。
一种方案是由独立的协调服务维护,比如可以存放在Zookeeper,所有节点注册到Zookeeper,由Zookeeper的通知机制,完成这种变化的通知。