Python探路-分布式系统

一、首先了解下集群、分布式和微服务之间的异同:

1、集群:

集群的意思就是将一个应用程序,部署到多台服务器上面,然后在这些服务器的前面通过负载均衡服务器来择优选择哪一台服务器去执行;集群的优点就是当其中的一个服务器宕机了,其他服务器可以接上继续工作;将应用程序部署在多台服务器时,也提供了数据的吞吐量。

 

2、分布式:

可以将分布式理解为,将某一个应用程序,拆分成多个模块来部署,各个模块负责不同的功能;分布式的优点是细化了应用程序的功能模块,同时也减轻了一个完整的应用程序部署在一台服务器上的负担,用了分布式拆分后,就相当于把一个应用程序的多个功能分配到多台服务器上去处理了。

3、微服务:

微服务是架构设计方式,分布式是系统部署方式,SOA架构强调的是异构系统之间的通信和解耦合,而微服务架构强调的是系统按业务边界做细粒度的拆分和部署。微服务架构强调的一个重点是“业务需要彻底的组件化和服务化”,原有的单个业务系统会拆分为多个可以独立开发、设计、运行的小应用。这些小应用之间通过服务完成交互和集成。微服务架构 = 80%的SOA服务架构思想 + 100%的组件化架构思想 + 80%的领域建模思想。

二、分布式要解决什么问题呢?

解决持久化数据太大,单个节点的硬盘无法存储的问题;解决运算量太大,单个节点的内存、CPU无法处理的问题。解决这些问题,有两种思路:scale up,scale out。前者是依赖提高单节点的能力,而后者是通过将业务部署到多个节点上来提高能力。

状态的维护比计算要难很多,所谓状态就是需要持久化的数据。因此主要考虑分布式存储,况且即使是分布式计算,为了节省带宽需要尽量保证data locality,也是需要分布式存储。这就需要考虑到数据的分片数据的冗余

三、数据的分片:

数据分片(segment,fragment, shard, partition),就是按照一定的规则,将数据集划分成相互独立、正交的数据子集,然后将数据子集分布到不同的节点上。

数据分片就能提供两个额外的好处:

  (1)提升性能和并发,操作被分发到不同的分片,相互独立

  (2)提升系统的可用性,即使部分分片不能用,其他分片不会受到影响

那有哪些数据分片的方式呢?一共有三种:hash方式,一致性hash(consistent hash),按照数据范围(range based)

1、hash方式:

假设特征值为K,节点数为N,那么该数据就会被保存到K MOD N的节点上

优点:简单,快速

缺点:1、万一增加和减少节点,那么就会有大量的数据进行迁移,这就违背了单调性(如果已经有一些内容通过哈希分派到了相应的缓冲中,又有新的缓冲加入到系统中,哈希的结果应能够保证原有已分配的内容可以被映射到原有的或者新的缓冲中去,而不会被映射到旧的缓冲集合中的其他缓冲区 )2、会导致负载不均匀,可能有的数据的数据量比较大,比如节点1、3、5被分配到1节点上、2、4、6被分配到节点2上,但是节点1都是数据量比较大的,就会导致数据存储不均衡。

2、一致性hash(consistent hash):

一致性hash是将数据按照特征值映射到一个首尾相接的hash环上,同时也将节点(按照IP地址或者机器名hash)映射到这个环上。对于数据,从数据在环上的位置开始,顺时针找到的第一个节点即为数据的存储节点。这里仍然以上述的数据为例,假设id的范围为[0, 1000],N0, N1, N2在环上的位置分别是100, 400, 800,那么hash环示意图与数据的分布如下:

Python探路-分布式系统

当增加一个节点时,比如600,那也只会影响到附近的节点,只会将NODE2上数据id为533的数据放到新节点3上,这样看增加/减少一个节点迁移的数据量相对较少,但是有一个问题,但增加一个节点分担的也只是附近的节点而已,没有做到真正的负载均衡,因此有人想到了虚拟节点,其实实际应用中,环上对应的是虚拟节点,当我们增加一个物理节点,会在环上分配好多个虚拟节点,这样就能够分摊其他很多节点的压力。这样看一致性hash无懈可击,但是它带来的就是元数据的增加,1、虚拟节点在hash环上的问题,且虚拟节点的数目又比较多;2、,虚拟节点与物理节点的映射关系

3、按照数据范围(range based)

简单来说,就是按照关键值划分成不同的区间,每个物理节点负责一个或者多个区间。其实这种方式跟一致性hash有点像,可以理解为物理节点在hash环上的位置是动态变化的。

还是以上面的数据举例,三个节点的数据区间分别是N0(0, 200], N1(200, 500], N2(500, 1000]。那么数据分布如下:

Python探路-分布式系统

区间的大小不是固定的,每个数据区间的数据量与区间的大小也是没有关系的。比如说,一部分数据非常集中,那么区间大小应该是比较小的,即以数据量的大小为片段标准。在实际工程中,一个节点往往负责多个区间,每个区间成为一个块(chunk、block),每个块有一个阈值,当达到这个阈值之后就会分裂成两个块。这样做的目的在于当有节点加入的时候,可以快速达到均衡的目的。

不知道读者有没有发现,如果一个节点负责的数据只有一个区间,range based与没有虚拟节点概念的一致性hash很类似;如果一个节点负责多个区间,range based与有虚拟节点概念的一致性hash很类似。

  range based的元数据管理相对复杂一些,需要记录每个节点的数据区间范围,特别单个节点对于多个区间的情况。而且,在数据可修改的情况下,如果块进行分裂,那么元数据中的区间信息也需要同步修改。

在这里对三种分片方式

 

映射难度 元数据 节点增删 数据动态均衡
hash方式 简单 非常简单,几乎不用修改 需要迁移的数据比较多 不支持
consistent hash
without virtual node
简单 比较简单,取决于节点规模,几乎不用修改 增删节点的时候只影响hash环上相邻节点,但不能使所有节点都参与数据迁移过程 不支持
consistent hash 
with virtual node
中等 稍微复杂一些,主要取决于虚拟节点规模,很少修改 需要迁移的数据比较少,且所有节点都能贡献部分数据 若支持(修改虚拟节点与物理节点映射关系)
range based 较为复杂 取决于每个块的大小,一般来说规模较大;且修改频率较高 需要迁移的数据比较少,且所有节点都能贡献部分数据 支持,且比较容易

元数据:

元数据服务器就像人类的大脑,一只手不能用了还没忍受,大脑不工作整个人就瘫痪了。因此,元数据服务器的高性能、高可用,要达到这两个目标,元数据服务器就得高可扩展 -- 以此应对元数据的增长。

这里以HDFS元数据为例:

HDFS中,元数据服务器被称之为namenode:

Python探路-分布式系统

上图中NN即NameNode, DN即DataNode(即实际存储数据的节点)。从图中可以看到, 两台 NameNode 形成互备,一台处于 Active 状态,为主 NameNode,另外一台处于 Standby 状态,为备 NameNode,只有主 NameNode 才能对外提供读写服务

Active NN与standby NN之间的数据同步通过共享存储实现,共享存储系统保证了Namenode的高可用。为了保证元数据的强一致性,在进行准备切换的时候,新的Active NN必须要在确认元数据完全同步之后才能继续对外提供服务。另外,Namenode的状态监控以及准备切换都是Zookeeper集群负责。

元数据缓存:

即使元数据服务器可以由一组物理机器组成,也保证了副本集之间的一致性问题。但是如果每次对数据的请求都经过元数据服务器的话,元数据服务器的压力也是非常大的

怎么达到缓存的强一致性呢?比较容易想到的办法是当metadata变化的时候立即通知所有的缓存服务器(mongos),但问题是通信有延时,不可靠。这里介绍下Lease机制:

首先为了更好的讲述Lease机制,这里称元数据服务器为服务器,缓存服务器为客户端。

1、服务器向所有客户端发送缓存数据的同时,颁发一个lease,lease包含一个有限期(即过期时间)

2、lease的含义是:在这个有效期内,服务器保证元数据不会发生变化

3、因此客户端在这个有效期内可以放心大胆的使用缓存的元数据,如果超过了有效期,就不能使用数据了,就得去服务器请求。

4、如果外部请求修改服务器上的元数据(元数据的修改一定在服务器上进行),那么服务器会阻塞修改请求,直到所有已颁发的lease过期,然后修改元数据,并将新的元数据和新的lease发送到客户端

5、如果元数据没有发生变化,那么服务器也需要在之前已颁发的lease到期之间,重新给客户端颁发新的lease(只有lease,没有数据)

四、数据冗余:

所谓的中心化,就是对于副本集的更新操作有一个中心节点来协调管理,将分布式的并发操作转化为单点的并发操作,从而保证副本集内各节点的一致性。其优点在于逻辑简单,将复杂的问题(分布式并发)转换成一个有成熟解决方案的问题(单点并发)。但缺点在于,副本集的可用性依赖于中心节点,如果中心节点故障,即使有中心节点自动切换机制,也会出现数10秒的不可用。

Python探路-分布式系统

而去中心化则是说副本集中没有中心节点,所有节点的地位是平等的,大家都可以接受更新请求,相互通过协商达成数据的一致。去中心化副本控制协议的最大好处在于可用性比较强,只要有大多数节点存活就能提供服务。但缺点时协议流程复杂,尤其是需要强一致性保证的时候。

对于数据冗余需要说明几个问题:

1、复制集之间数据的同步是同步模式还是异步模式。

所谓同步(Synchronous replication),就是说对于客户端请求,系统阻塞到复制集中所有节点都更新完成,才能向客户端返回,即write all。而异步(Asynchronous replication)模式,只要一个或者部分节点更新则算写入操作成功,通常是write one。

很明显,在同步模式下,系统的可靠性非常好,但是响应延迟比较大,异步模式下,系统的吞吐量会比较好,但是存在数据丢失的风险

2、数据的流向:

1、如果使用链式传输数据,写入过程中每个节点的带宽利用都比较均衡,可以充分利用网络资源,也不会有单点压力;但是需要经过多个节点,写入延迟会比较大。2、如果在主从模式下,Primary节点的带宽压力比较大,但是写入延迟会小一些。