DDIA学习笔记3——chapter6:分区(Sharding/Partition)

DDIA_Chapter6  学习笔记

​ Partition主要是为了可扩展性,不同的Partition可以放在不共享集群中的不同节点上。因此,大数据集可以分布在多个磁盘上,并且查询负载可以分布在多个处理器上。

​ 实际上,每个Partition都是自己的小型数据库,尽管数据库可能支持同时进行多个分区的操作。

Partition&Replication:

​ Partition通常与Replication结合使用,使得每个Partition的副本存储在多个节点上。 这意味着,即使每条记录属于一个Partition,它仍然可以存储在多个不同的节点上以获得容错能力。

​ 一个节点可能存储多个Partition。 如果使用Master&Slave复制模型,则Partition和Replication的组合如下图所示。 每个Partition的Master被分配给一个节点,Follower被分配给其他节点。 每个节点可能是某些Partition的Master,同时是其他Partition的Follower。

 Partition算法:

基于主键索引(聚簇索引)的分区算法:

聚簇索引记录了和数据之间一一对应的关系,可以根据聚簇索引直接查询到整条数据。

范围分区算法:为每个分区指定一块连续的键范围(从最小值到最大值),如纸百科全书的卷。如果知道范围之间的边界,则可以轻松确定哪个分区包含某个值。如果您还知道分区所在的节点,那么可以直接向相应的节点发出请求(对于百科全书而言,就像从书架上选取正确的书籍)。但是,键的范围不一定均匀分布,因为数据也很可能不均匀分布,为了均匀分配数据,分区边界需要依据数据调整。而且,某些特定的访问模式会导致热点,如果主键是时间戳,则分区对应于时间范围,例如,给每天分配一个分区。 不幸的是,由于我们在测量发生时将数据从传感器写入数据库,因此所有写入操作都会转到同一个分区(即今天的分区),这样分区可能会因写入而过载,而其他分区则处于空闲状态。

散列分区算法:由于偏斜和热点的风险,许多分布式数据存储使用散列函数来确定给定键的分区。但是,使用键散列进行分区,我们失去了键范围分区的一个很好的属性:高效执行范围查询的能力。曾经相邻的密钥现在分散在所有分区中,所以它们之间的顺序就丢失了。而且,散列分区算法虽然可以帮助减少热点,却不能完全避免它们:在极端情况下,所有的读写操作都是针对同一个键的,所有的请求都会被路由到同一个分区。

大部分数据库无法自动处理这种高倾斜度的负载。因此,我们需要在应用程序层面处理这样的问题,一个很简单的做法是——如果一个主键被认为是非常火爆的,则在主键的开始或结尾添加一个随机数。只要一个两位数的十进制随机数就可以将主键分散为100种不同的主键,从而存储在不同的分区中。(yb:这种做法的意义是什么?不太理解啊。比如关于我的数据原本只有一条,这样被分成了100条数据后,对于我的数据修改应该如何完成?这样只能处理读请求吧?

基于普通索引(非聚簇索引)的分区算法:

非聚簇索引没有记录和数据之间的对应关系,只能根据该类索引查找到聚簇索引和该索引对应的字段,如果要查询整条数据则需要根据查找到的聚簇索引进行二次查找(回表)。

基于文档的分区算法:对于非聚簇索引的分区而言,我们首先对聚簇索引进行分区(范围分区或哈希分区),进而指向某个分区的非聚簇索引则成为一个分区。但是,值得注意的是,非聚簇索引有时不仅仅会只存在于一个分区中,因此查询时需要查询所有分区并聚合查询结果,如图:

 基于关键字(Term)的分区算法:和聚簇索引的分区不对应,非聚簇索引自身进行分区。

 Rebalance:

当我们需要添加、减少Partition时;当我们需要替换Partition原本的故障机器时。我们都需要将这个Partition的数据和访问请求转移到其他结点,这个过程被成为Partition Reblance

 Rebalance的基本需求:1.Rebalance完成后,Partition之间的数据和请求不应产生倾斜;2.当Rebalance进行时,不能停机;3.效率高。

 Rebalance策略:

错误的策略:hash Mod N算法——将所有数据、请求hash后分配给特定Partition。这种策略的缺点是当Rebalance产生式,需要移动大量数据,效率低下。

正确的策略:

1.预分配策略——预先分配固定数量的Partition,预先分配的Partition数量大于结点数量。当结点数量增加时:新增的结点将一部分Partition“放入”自身;当结点数量减少时:减少的结点将自身结点”“放入”其他结点;当结点替换时,新结点将原结点的Partition“放入”自身。在这种配置中,分区的数量通常在数据库第一次建立时确定,之后不会改变。如果数据集的总大小难以预估(例如,如果它开始很小,但随着时间的推移可能会变得更大),选择正确的分区数是困难的。由于每个分区包含了总数据量固定比率的数据,因此每个分区的大小与集群中的数据总量成比例增长。如果分区非常大,再平衡和从节点故障恢复变得昂贵。但是,如果分区太小,则会产生太多的开销。当分区大小“恰到好处”的时候才能获得很好的性能,如果分区数量固定,但数据量变动很大,则难以达到最佳性能。

2.动态分区策略——当分区增长到超过配置的大小时(在HBase上,默认值是10GB),会被分成两个分区,每个分区约占一半的数据【26】。与之相反,如果大量数据被删除并且分区缩小到某个阈值以下,则可以将其与相邻分区合并。此过程与B树类似。动态分区的一个优点是分区数量适应总数据量。如果只有少量的数据,少量的分区就足够了,所以开销很小;如果有大量的数据,每个分区的大小被限制在一个可配置的最大值。需要注意的是,一个空的数据库从一个分区开始,因为没有关于在哪里绘制分区边界的先验信息。数据集开始时很小,直到达到第一个分区的分割点,所有写入操作都必须由单个节点处理,而其他节点则处于空闲状态。

3.按结点比例分区——对于预分配的分区,每个Partition的大小与数据集的大小成正比;对于动态分区,Partition的数量与数据集的大小成正比,因为拆分和合并过程将每个Partition的数据量保持在固定的最小值和最大值之间。因此,在这两种情况下,分区的数量都与节点的数量无关。Cassandra和Ketama使用的第三种方法是使分区数与节点数成正比——换句话说,每个节点具有固定数量的分区【23,27,28】。在这种情况下,每个分区的大小与数据集大小成比例地增长,而节点数量保持不变,但是当增加节点数时,分区将再次变小。由于较大的数据量通常需要较大数量的节点进行存储,因此这种方法也使每个分区的大小较为稳定。

请求路由:当客户想要发出请求时,如何知道要连接哪个节点?

方案1——Round-Robin Load Balancer,如果数据库节点恰巧拥有请求的Partition,则它可以直接处理该请求;否则,它将请求转发到适当的节点,接收回复并传递给客户端。

方案2——增加一个路由层,专门进行请求转发。

方案3——让用户知道访问哪个结点

通常采用外部服务,例如ZK记录结点和对应分区的映射关系,并将客户请求转发到正确的结点。

猜你喜欢

转载自www.cnblogs.com/ybonfire/p/12202060.html