什么是分布式系统
分布式系统(distributed system)具有高度的 内聚性 和 透明性。
内聚性:每一个节点高度自治,有本地的数据库管理系统;
透明性:每一个数据库分布节点对用户来说是透明的,用户是感觉不到"分布"的,即用户不需要知道关系是否分割、有无副本、数据位于哪个节点、事物在哪个站点上执行等;
CAP原理
c:一致性(Consistency)
在分布式系统中的所有数据备份,在同一时刻是否同样的值;
a:可用性(Availability)
在集群中一部分节点故障后,集群整体是否还能响应客户端的读写请求;
p: 分区 容忍性(Partition Rolerance)
分区相当与对通信的时限要求,消息体如果不能在时限内达成数据一致性,意味着发生了分区的情况。
因此分区容忍性是基本要求,否则就失去了价值。因此分布式数据系统,就是在一致性和可用性之间取一个平衡。对于大多数的web应用,并不需要强一致性,通常做法是以强一致性为代价换取高可用,这也是多数分布式数据库产品的方向。
这里的牺牲一致性,指的是不再要求关系型数据库中的强一致性,只要能达到最终一致性即可,这个时间窗口对用户来说是透明的,用户是感知不到的。通常的做法,是通过多份异步复制来实现系统的高可用和数据的最终一致性,时间窗口取决于数据复制到一致状态的时间。
关于最终一致性
一致性问题是因为出现了并发的读操作或者写操作。对于客户端,多进程并发访问时,更新过的数据在不同进程中如何获取的不同策略,决定了不同的一致性。
强一致性:对于关系型数据库,要求更新过的数据能被后续的访问都能看到;
弱一致性:如果容忍后续的部分或者全部访问不到,这就是弱一致性;
最终一致性:如果经过一段时间之后,要求能够访问到更新后的数据,就是最终一致性;
从数据的同步看强弱一致性
对于服务器而言,如何尽快将更新后的数据分布到整个系统,降低最终一致性的时间窗口,对分布式系统来说是十分重要的。
我们假设,现在有一个分布式系统,数据保存了 N 份,更新数据需要保证写完成的节点数为 W ,读取数据时需要读取的节点数为 R :
如果 W+R > N, 说明读写节点重复,则是强一致性,如一主一备同步复制的关系型数据库;
如果 W+R <= N, 则是弱一致性,比如一主一备异步复制的数据库,读取备库,可能无法读取主库已经更新过的数据。
对于分布式数据库而言,为了保证高可用,一般设置 N >= 3。
分布式系统架构思路
两种思路
1. 现在有一台服务器,一台服务器可以处理 100w/s 的请求,随着业务增长,据估算,访问量最高会达到 200w/s ,如果不进行处理,服务器会拒绝访问,甚至会 出现宕机。最简单的方案,就是再增加一台机器(在实际环境中,增加机器来解决问题是常用的一种解决方案),每台机器承担一半的请求,如果访问量继续增加的话,可以继续通过增加机器来解决问题。这就是水平扩展。这里暂时不讨论如何进行负载的均衡;
2. 现在有一个应用对外提供项服务,每项服务都是一个请求,当前服务器可以承担 100w/s 的请求,目前统计, A 服务 40w/s , B 服务 40w/s。业务同样扩大,服务 A 和服务 B 的请求都增加了一倍,有需要进行扩展。使用两台机器进行平分,每台机器承担服务 A 和服务 B 各一半,平分的话太复杂,不如一台机器只负责服务 A, 亮一台机器只负责业务 B,这种方式叫做垂直扩展。
简单对水扩展和垂直扩展进行总结,可以发现,按照业务进行拆分,即是垂直扩展;按请求进行拆分,即水平扩展。
负载均衡
负载均衡要做的任务,就是确定客户端的请求,应该发往分布式系统中的哪一台服务器上,通常的做法,就是通过一台中间服务器,来实现请求的分配。
常见的负载均衡策略:
1. FastDFS 分布式系统:client 向 tracker 询问一台可以进行文件下载的 storage ,tracker 返回一台可用的 storage,client 直接和 storage 进行通信完成文件下载;
这里的 tracker 就是负载均衡服务器;
2. 分布式RPC中间件 Hedwig:client 向 zookeeper询问哪台服务器可以执行请求;zookeeper 返回一台可用的 server;client 直接和 service 完成通信。
zookeeper 是分布式系统中一个负载均衡框架,Google 的 chubby 的一个开源实现,是 Hadoop 和 Hbase 的重要组件
3. nginx也是一个负载均衡服务器,面向的是分布式web服务器
负载均衡调度算法
1. 轮询
使用这种方式,所有标记进入虚拟服务的服务器应该有相近的资源容量以及负载形同的应用程序。
2. 加权轮循(Weighted Round Robin)
解决了轮询的缺点,提前为每台服务器根据资源及能力大小分配权重,传入的请求按顺序,根据权重,顺序分配给集群中的服务器。
3. 最少连接数
上面两种方式,都不能够确定系统不能识别在给定的时间里保持了多少连接。由于不同连接处理的时间不同,可能发生服务器 A 上处理的连接少于 B,但是A已经超载,因为A上用户打开连接持续的时间更长。
这种潜在的问题,可以通过“最少连接数”算法来避免,客户端的请求是根据每台机器当前打开的连接数来分配的,即活跃连接数最少的服务器会自动接收下一个传入的请求。
与简单轮询一样,每台服务器也需要有相近的资源容量以及负载形同的应用程序;需要注意的是,在流量率低的配置环境中,个服务器的流量并不相同,会有限考虑第一台服务器。
4. 加权最少连接
如果服务器资源容量各不相同,那么“加权最少连接”更为合适。这种分配方式结合了连接和权重两者的优势,大多数情况下,这是一种相当公平的算法。
同样,这种方式需要和最少连接数注意相同的问题。
5. 最少连接数慢启动时间
对于方式 3 和方式 4 来说,当集群中新增一个节点后,可以为其配置一个时间段,这个段时间内连接数是有限制的,而且是缓慢增加的,这为服务器提供了一个“过度时间”。
6. 源 IP 哈希
通过生成源 IP 的哈希值,并通过这个哈希值来找到正确的真实服务器,意味着对于同一主机的请求,对应的服务器总是相同的,但是这种方式会导致服务器负载不均衡。