集群对于数据的处理

概论

在it时代不断演进的大背景下,单纯靠单机(单节点)来维护数据已经不适用了,把对于数据的处理按照规则均分到不同机器(多节点)的好处是不言而喻的:

  • 可扩展性,如果数据量、读取负载、写⼊负载超出单台机器的处理能⼒,可以将负载分散到多台计算机上。
  • 容错/⾼可⽤性,如果应⽤需要在单台机器(或多台机器,⽹络或整个数据中⼼)出现故障的情况下仍然能继续⼯作,则可使⽤多台机器,以提供冗余。⼀台故障时,另⼀台可以接管。
  • 延迟,如果在世界各地都有⽤户,你也许会考虑在全球范围部署多个服务器,从⽽每个⽤户可以从地理上最近的数据中⼼获取服务,避免了等待⽹络数据包穿越半个世界。

而这种处理方式就是集群。

在集群中,数据分布在多节点上有两种常⻅的⽅式:

  • 复制(Replication),在⼏个不同的节点上保存数据的相同副本,可能放在不同的位置。 复制提供了冗余:如果⼀些节点不可⽤,剩余的节点仍然可以提供数据服务。
  • 分区 (Partitioning),将⼀个⼤型数据拆分成较⼩的⼦集(称为分区(partitions)),从⽽不同的分区可以指派给不同的节点(node)(亦称分⽚(shard))。

复制

复制意味着在通过⽹络连接的多台机器上保留相同数据的副本。我们希望通过复制数据,来实现一些有价值的措施:

  • 使得数据与⽤户在地理上接近(从⽽减少延迟)
  • 即使系统的⼀部分出现故障,系统也能继续⼯作(从⽽提⾼可⽤性)
  • 扩展可以接受读请求的机器数量(从⽽提⾼读取吞吐量)

目前有三种流⾏的变更复制算法:单领导者(single leader),多领导者(multi leader)和⽆领导者(leaderless)。一般情况下,单领导者是⾮常流⾏的,因为它很容易理解,不需要担⼼冲突解决。在出现故障节点,⽹络中断和延迟峰值的情况下,多领导者和⽆领导者复制可以更加稳健,但以更难以推理并仅提供⾮常弱的⼀致性保证为代价。

领导者与跟随者

存储数据副本的每个节点称为副本(replica)。当存在多个副本时,会不可避免的出现⼀个问题:如何确保所有数据都落在了所有的副本上?
每⼀次的写⼊数据操作都需要传播到所有副本上,否则副本就会包含不⼀样的数据。最常⻅的解决⽅案被称为基于领导者的复制(leader-based replication)(也称主动/被动(active/passive) 或主/从(master/slave)复制)。它的⼯作原理如下:

  1. 副本之⼀被指定为领导者(leader、master)。当客户端要写⼊数据时,它必须将请求发送给领导者,领导者会将新数据写⼊其本地存储。
  2. 其他副本被称为追随者(followers、read replicas、hot-standby)。每当领导者将新数据写⼊本地存储时,它也会将数据变更发送给所有的追随者,称之为复制⽇志(replication log)记录或变更流

(change stream)。每个跟随者从领导者拉取⽇志,并相应更新其本地数据副本,⽅法是按照领导者处理的相同顺序应⽤所有写⼊。
当客户想要从集群中读取数据时,它可以向领导者或追随者查询。 但只有领导者才能接受写操作(从客户端的⻆度来看从库都是只读的)。

同步复制与异步复制

复制算法的⼀个重要细节是:复制是同步进行还是异步进行。

同步复制的优点是,从节点保证有与主节点⼀致的最新数据副本。如果主节点突然失效,我们可以确信这些数据仍然能在从节点上上找到。缺点是,如果同步从节点没有响应(⽐如它已经崩溃,或者出现⽹络故障,或 其它任何原因),主节点就⽆法处理写⼊操作。主节点必须阻⽌所有写⼊,并等待同步副本再次可⽤。

因此,将所有从节点都设置为同步的是不切实际的:任何⼀个节点的中断都会导致整个系统停滞不前。实
际上,如果在数据中心启⽤同步复制,通常意味着其中⼀个跟随者是同步的,⽽其他的则是异步的。如
果同步从节点变得不可⽤或缓慢,则使⼀个从节点把异步改成同步,这保证你⾄少在两个节点上拥有最新的数据副本:主节点和同步从节点,这种配置有时也被称为半同步(semi-synchronous)。

设置新从节点

总会有新的从节点的加入:也许是为了增加副本的数量,或替换失败的节点,那如何确保新的从节点拥有主节点数据的精确副本?
简单地将数据⽂件从⼀个节点复制到另⼀个节点通常是不够的:客户端不断向主节点写⼊数据,数据总是在不断变化,标准的数据副本会在不同的时间点总是不⼀样,复制的结果可能没有任何意义。

我们当然可以粗暴点,通过锁定主节点的写⼊来使磁盘上的⽂件保持⼀致,但是这会违背⾼可⽤的⽬标。所以我们可以这么去做:

  1. 在某个时刻获取主节点的⼀致性数据快照文件(如果可能),⽽不必锁定。
  2. 将快照复制到新的从节点。
  3. 从节点连接到主节点,并拉取快照之后发⽣的所有数据变更,这要求快照与主节点复制⽇志中的位置精确
    关联。
  4. 当从节点处理完快照之后积压的数据变更,我们说它赶上(caught up)了主节点。现在它可以继续处理主库产⽣的数据变化了。

节点宕机

集群中的任何节点都可能宕机,可能因为意外的故障,也可能由于计划内的维护。对运维⽽⾔,能在集群不中断服务的情况下重启单个节点好处多多。我们的⽬标是,即使个别节点失效,也能保持整个集群运⾏,并尽可能控制节点停机带来的影响。

从节点宕机

在其本地磁盘上,每个从节点记录从主节点收到的数据变更。如果从节点崩溃并重新启动,或者,如果主节点和
从节点之间的⽹络暂时中断,则⽐较容易恢复:从节点可以连接到主及诶点,并请求在从节点断开连接时发⽣的所有数据变更。当应⽤完所有这些变化后,它就赶上了主节点,并可以像以前⼀样继续接收数据变更流。

主节点宕机

主节点宕机处理起来相当棘⼿:其中⼀个从节点需要被提升为新的主节点,需要重新配置客户端,以将它们的
写操作发送给新的主节点,其他从节点需要开始拉取来⾃新主节点的数据变更。这个过程被称为故障切换
(failover)。
故障切换可以⼿动进⾏(通知管理员主节点挂了,并采取必要的步骤来创建新的主节点)或⾃动进⾏。⾃动
故障切换过程通常由以下步骤组成:

  1. 确认主节点宕机。有很多事情可能会出错:崩溃,停电,⽹络问题等等。没有万⽆⼀失的⽅法来检测
    出现了什么问题,所以⼤多数系统只是简单使⽤超时(Timeout):节点频繁地相互来回传递消
    息,并且如果⼀个节点在⼀段时间内(例如30秒)没有响应,就认为它挂了(因为计划内维护⽽
    故意关闭主节点不算)。
  2. 选择⼀个新的主节点。这可以通过选举过程(主节点由剩余副本以多数选举产⽣)来完成,或者可以由
    之前选定的控制器节点(controller node)来指定新的主节点。主节点的最佳⼈选通常是拥有旧主节点
    最新数据副本的从节点(最⼩化数据损失)。
  3. 重新配置系统以启⽤新的主节点。客户端现在需要将它们的写请求发送给新主节点。如果⽼主节点恢复了,可能仍然认为⾃⼰是主节点,没有意识到其他副本已经让它下台了,就会导致一种叫脑裂(split brain)的行为,所以系统需要确保⽼主节点认可新主节点,成为⼀个从节点。

分区

分区通常与复制结合使⽤,使得每个分区的副本存储在多个节点上。 这意味着,即使每条记录属于⼀个分区,它仍然可以存储在多个不同的节点上以获得容错能⼒。
⼀个节点可能存储多个分区。 如果使⽤主从复制模型, 每个分区领导者(主)被分配给⼀个节点,追随者(从)被分配给其他节点。 每个节点可能是某些分区的领导者,同时是其他分区的追随者。

数据的分区

假设你有⼤量数据并且想要分区,如何决定在哪些节点上存储哪些记录呢?

分区⽬标是将数据和查询负载均匀分布在各个节点上。如果每个节点公平分享数据和负载,那么理论上10个节点应该能够处理10倍的数据量和10倍的单个节点的读写吞吐量(暂时忽略复制)。
如果分区是不公平的,⼀些分区⽐其他分区有更多的数据或查询,我们称之为偏斜(skew)。数据偏斜的存在使分区效率下降很多。在极端的情况下,所有的负载可能压在⼀个分区上,其余9个节点空闲的,瓶颈落在这⼀个繁忙的节点上。不均衡导致的⾼负载的分区被称为热点(hot spot)。

避免热点最简单的⽅法是将记录随机分配给节点。这将在所有节点上平均分配数据,但是它有⼀个很⼤的缺点:当你试图读取⼀个特定的值时,你⽆法知道它在哪个节点上,所以你必须并⾏地查询所有的节点。

根据键的范围分区

⼀种分区的⽅法是为每个分区指定⼀块连续的键范围(从最⼩值到最⼤值)。如果知道范围之间的边界,则可以轻松确定哪个分区包含某个值。
然⽽,这种分区的缺点是某些特定的访问模式会导致热点。 如果键是时间戳,则分区对应于时间范围(上中下午三个分区),则会导致同个时间所有写⼊操作都会转到同⼀个分区,这样分区可能会因写⼊⽽过载,⽽其他分区则处于空闲状态。

根据键的哈希分区

由于偏斜和热点的⻛险,许多分布式数据使⽤哈希函数来确定给定键的分区。⼀个好的焊锡函数可以将偏斜的数据均匀分布。假设你有⼀个32位散列函数,⽆论何时给定⼀个新的字符串输⼊,它将返回⼀个0到 2 32 2^{32} 232 -1之间的"随机"数,即使输⼊的字符串⾮常相似,它们的散列也会均匀分布在这个数字范围内。

不幸的是,通过使⽤哈希进⾏分区,我们失去了键范围分区的⼀个很好的属性:⾼效执⾏范围查询的能⼒。
哈希分区可以帮助减少热点,但是,它不能完全避免它们:在极端情况下,所有的读写操作 都是针对同⼀个键的,所有的请求依然都会被路由到同⼀个分区。

分区再平衡

随着时间的推移,数据会有各种变化,这些变化会导致数据和请求从⼀个节点移动到另⼀个节点。 将负载从集群中的⼀个节点向另⼀个节点移动的过程称为再平衡(reblancing)。

⽆论使⽤哪种分区⽅案,再平衡通常都要满⾜⼀些最低要求:

  • 再平衡之后,负载(数据存储,读取和写⼊请求)应该在集群中的节点之间公平地共享。
  • 再平衡发⽣时,集群应该继续接受读取和写⼊。
  • 节点之间只移动必须的数据,以便快速再平衡,并减少⽹络和磁盘I/O负载。
固定分区的平衡策略

创建⽐节点更多的分区,并为每个节点分配多个分区。例如,运⾏在10个节点的集群上可能会从⼀开始就被拆分为1,000个分区,因此⼤约有100个分区被分配给每个节点。
现在,如果⼀个节点被添加到集群中,新节点可以从当前每个节点中窃取⼀些分区,直到分区再次公平分配,如果从集群中删除⼀个节点,则会发⽣相反的情况。
只有分区在节点之间的移动,分区的数量不会改变,键所指定的分区也不会改变,唯⼀改变的是分区所在的节点。这种变更并不是即时的(在⽹络上传输⼤量的数据需要⼀些时间 ),所以在传输过程中,原有分区仍然会接受读写操作。

如果数据的总⼤⼩难以预估(例如,如果它开始很⼩,但随着时间的推移可能会变得更⼤),选择正确的分区数是困难的。由于每个分区包含了总数据量固定⽐率的数据,因此每个分区的⼤⼩与集群中的数据总量成⽐例增⻓。如果分区⾮常⼤,再平衡和从节点故障恢复变得昂贵。但是,如果分区太⼩,则会产⽣太多的开销。当分区⼤⼩“恰到好处”的时候才能获得很好的性能,如果分区数量固定,但数据量变动很⼤,则难以达到最佳性能。

动态分区的平衡策略

当分区的数据量增⻓到超过配置的⼤⼩时,会被分成两个分区,每个分区约占⼀半的数据。与之相反,如果⼤量数据被删除并且分区缩⼩到某个阈值以下,则可以将其与相邻分区合并,此过程与B树顶层发⽣的过程类似。
每个分区分配给⼀个节点,每个节点可以处理多个分区,就像固定数量的分区⼀样。⼤型分区拆分后,可以将其中的⼀半转移到另⼀个节点。

动态分区的⼀个优点是分区数量适应总数据量。如果只有少量的数据,少量的分区就⾜够了,所以开销
很⼩;如果有⼤量的数据,每个分区的⼤⼩被限制在⼀个可配置的最⼤值。

按节点⽐例分区的平衡策略

通过动态分区,分区的数量与数据的⼤⼩成正⽐,因为拆分和合并过程将每个分区的⼤⼩保持在固定的最⼩值和最⼤值之间。另⼀⽅⾯,对于固定数量的分区,每个分区的⼤⼩与数据的⼤⼩成正⽐。在这两种情况下,分区的数量都与节点的数量⽆关。

第三种⽅法是使分区数与节点数成正⽐——换句话说,每个节点具有固定数量的分区。在这种情况下,每个分区的⼤⼩与数据⼤⼩成⽐例地增⻓,⽽节点数量保持不变,但是当增加节点数时,分区将再次变⼩。由于较⼤的数据量通常需要较⼤数量的节点进⾏存储,因此这种⽅法也使每个分区的⼤⼩较为稳定。
当⼀个新节点加⼊集群时,它随机选择固定数量的现有分区进⾏拆分,然后占有这些拆分分区中每个分区的⼀半,同时将每个分区的另⼀半留在原地。随机化可能会产⽣不公平的分割,但是平均在更⼤数量的分区上时,新节点最终从现有节点获得公平的负载份额。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

我叫小八

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值