引言
为什么需要数据分区,在我们面临海量数据的时候,单个节点显然是无法承受所有数据的存储以及查询的,这个时候我们需要将数据拆分,这就是分区.它的定义是这样的,即:每一条数据只属于某一个特定的分区,这样每一个分区都可当做一个完整的数据库.虽然是这样,每个节点仍会存有副本以提高容错性,就类似于一个单主节点的复制.
分区的目的在于提高可扩展性和性能.分区以后一个大数据集就可以分散在更多的机器中,这也使得负载分散开了.而且每个分区是可以执行独立的查询的,这样可以做到跨节点的并行处理,这也就意味着理想情况下集群的性能是可以线性提升的.
分区的几种方式
数据分区的重点就是均匀分配,如果可以做到真正意义上的均衡,理论上就是线性的处理能力提升,但是如果负载并不均衡的话,可能会导致一个节点已经达到极限,其他的节点还是空闲状态,这样就丧失了我们使用分区的意义了,还有一个很大的缺点就是如何知道特定顺序存储在哪里?难道一定要把全部节点查询一遍吗?
- 基于关键字:也许叫范围分区更好,为每个分区分配一段关键字,相当与每个节点都是一个区间,通过加入的数据处于那个区间,我们可以很容易的进行特定数据的查询.关键字当然也不一定是均匀的,因为其中的数据就可能不是均匀的,具体怎么分配还是要看数据的分布.在分区内部还可以按照关键字(比如时间戳)进行排序,这样也可以支持区间查询,即以关键字为索引.这样的缺点是是可能使得某些访问模式会导致热点查询,一个系列写入导致所有的数据都插入了一个分区,这样使得一个分区是高负荷,其他分区则是空闲的,所以在选择关键字的时候需要仔细考虑,不要已单独的时间戳为关键字.
- 基于哈希值:这是一个非常常见的方式.首先好的哈希函数可以确保数据均匀分布.我们可以给每一个分区分配一个范围,哈希值在其范围内就存储在这个分区中.这样当然也有坏处,就是不支持范围查询,因为逻辑上相邻的数据可能哈希值相差十万八千里.这样我们要么不支持范围查询,要么就把请求发送给所有的节点.Redis的做法是如果一个查询的数据分配在不同节点,直接拒绝这个语句.基于哈希值的分区最著名的当属一致性哈希算法了.当然还有Redis集群使用的哈希槽,他们的本质其实都是一样的,就是避开了对总结点数的依赖.
当然还有一种折中的算法,就是关键字的一部分用于哈希分区,一部分用作在分区内排序,这样就可以在确定分区以后进行范围查询了.
分区与二级索引
二级索引(MySQL中CREATE INDEX一下的那个东西)通常不能唯一标识一条记录,而是用来加速特定值的查询,二级索引带来的主要挑战是它们不能规整地映射到分区中,因为索引一般是包含所有已知项的,而这些值此时分布在多个结点.有两种主要的方法来支持对二级索引进行分区:基于文档的分区和基于词条的分区.说成大白话就是每个结点建立一个索引或者一个大索引分配在不同的结点.
- 基于文档的分区:就是每个分区完全独立,分别维护自己的分区,这样在单个分区内可加速查询,但是如果数据分布在多个结点的话我们需要向每个结点发出消息,然后在客户端合并.这种方法也被称作scatter/gather,确实在范围查询的时候可能造成读的延迟.
- 基于词条的分区:对所有的数据构建全局索引,而不是每个分区维护自己的本地索引.而且,为了避免成为瓶颈,不能将全局索引存储在一个节点上,否则就破坏了设计分区均衡的目标.所以,全局索引也必须进行分区,且可以与数据关键字采用不同的分区策略.这样的优点是在查询时只需要向包含索引的分区发送读请求就可以了,缺点也很明显,就是更新代价很大,可能一个更新涉及到多个节点的更新,这样使得写的代价过高.
分区平衡
这个用大白话说就是在新加入节点时如何使得分区平衡.因为我们的集群是动态的,所以会在运行中遇到以下问题:
(1)查询压力增大,因此需要更多的CPU来处理负载。
(2)数据规模增加,因此需要更多的磁盘和内存来存储数据。
(3)节点可能出现故障,因此需要其他机器来接管失效的节点。
所以我们需要把数据从一个节点迁移到另外一个节点.但需要满足以下几点:
(1)平衡之后,负载、数据存储、读写请求等应该在集群范围内更均匀分布。
(2)再平衡执行过程中,数据库应该可以继续正常提供读写服务。
(3)避免不必要的负载迁移,以加快动态再平衡,并尽量减少网络和磁盘I/O影响。
为了满足以上要求.有以下集中方法:
- 固定数量分区:一致性哈希和哈希槽都使用了这样的方法,分区不取决于节点数量可以使得在加入新节点的时候可以最少的迁移数据.
- 动态分区:使用关键字分区时如果边界设置有问题的,可能使得一个分区的数据超限而其他分区数据很少,这个时候就使用了动态分区这种方法.当分区的数据增加超过一个可配的参数阈值,它就拆分成两个分区.每个承担一半的数据量.它的优点是可以自适应配置数据量,当数据少的时候分区也少,当数据多的时候数据是分配在多个节点,且数据都不超过配置值.
- 按节点比例分区:也就是每个节点拥有固定数量分区,当一个节点加入集群的时候对已有的节点数据进行分裂,拿走其中一部分存储在新加入的节点中,一致性哈希中就是这样的.
如何找到数据存储的节点
这是一个问题,因为数据是动态的,且在前面我们已经分区,我们如何知道该请求哪个节点呢?
- 允许发送请求给任何节点,不在的话进行转发(gossip协议同步数据信息).Redis集群就是这样做的.
- 将请求发送到一个路由层,类似于代理服务器.其负责转发请求.ZooKeeper负责更新代理的信息.
- 客户端感知节点和分区的分配关系直接查找.(这样的话需要分区变化不明显否则就需要其他工具,比如上面)
redis分区?
写完这个文章想起了前几天看到的一个面试题,redis分区的好处?第一次看到这个问题的时候确实是有点懵逼的,我想大家看到这个问题的时候把它当做另一个问题可能会好一点,即redis集群的好处?毕竟分区某种意义上来说就是分布式.
参考:
https://thinkwon.blog.csdn.net/article/details/103522351#RedisCPU_705