这篇文章是根据《分布式学习最佳实践:从分布式系统的特征开始(附思维导图)》这篇博客写的随心记。这里只供自己学习,请有志于学习的跳转上面的文章。
分布式系统是一个很大的概念,系统的学习非常的重要,所以最好先从本质出发来看分布式系统。虽然各个分布式系统的算法可能不一样,但是其本质的分布式特征却是一致的。
分布式的四大特征:可扩展性、高性能、高可用、一致性。这几个特性也是分布式系统的衡量指标,正是为了在不同的程度上满足这些特性(或者说达到这些指标),才会设计出各种各样的算法、协议,然后根据业务的需求在这些特性间平衡。
可拓展性
可拓展性是分布式系统必须的,因为分布式系统是由一组通过网络进行通信、为了完成共同的任务而协调工作的计算机节点组成的系统,所以在系统增加任务的时候能够增加资源来应对任务增长,达到自适应的能力。
扩展性的目标是使得系统中的节点都在一个较为稳定的负载下工作,这就是负载均衡,当然,在动态增加节点的时候,需要进行任务(可能是计算,可能是数据存储)的迁移,以达到动态均衡。
这时候就要考虑如何对任务拆分了,任务的拆分就是数据分片,即按照一定的规则将数据集划分成相互独立,正交的数据子集,然后将数据子集分布到不同的节点上。
数据分片有三种方式:hash方式,一致性hash(consistent hash),按照数据范围(range based)。对于任何方式,都需要思考以下几个问题:1.具体如何划分原始数据集?2.当原问题的规模变大的时候,能否通过增加节点来动态适应?3.当某个节点故障的时候,能否将该节点上的任务均衡的分摊到其他节点?4.对于可修改的数据(比如数据库数据),如果某节点数据量变大,能否以及如何将部分数据迁移到其他负载较小的节点,及达到动态均衡的效果?5.元数据的管理(即数据与物理节点的对应关系)规模?元数据更新的频率以及复杂度?
hash方式:按照数据的某一特征(key)来计算哈希值,并将哈希值与系统中的节点建立映射关系,从而将哈希值不同的数据分布到不同的节点上。但hash方式的缺点是:当加入或者删除一个节点的时候,大量的数据需要移动,而且很难解决数据不均衡的问题。
一致性hash:将数据按照特征值映射到一个首尾相接的hash环上,同时也将节点(按照IP地址或者机器名hash)映射到这个环上。一致性hash方式在增删的时候只会影响到hash环上响应的节点,不会发生大规模的数据迁移。缺点是:一致性hash方式在增加节点的时候,只能分摊一个已存在节点的压力;同样,在其中一个节点挂掉的时候,该节点的压力也会被全部转移到下一个节点。我们希望的是“一方有难,八方支援”,因此需要在增删节点的时候,已存在的所有节点都能参与响应,达到新的均衡状态。在实际工程中,一般会引入虚拟节点,即让一个物理节点管理多个虚拟节点,虚拟节点入环。这样在物理节点失效的时候,可以有多个节点来分配这个失效节点原本持有的虚拟节点,不过这也带来了不好管理分配的坏处。
range based:按照关键值划分成不同的区间,每个物理节点负责一个或者多个区间。其实这种方式跟一致性hash有点像,可以理解为物理节点在hash环上的位置是动态变化的。区间的大小不是固定的,每个数据区间的数据量与区间的大小没有关系,但是与区间的数量有关系,当一个区间的数据量达到一个阈值的时候,区间会变成两个。如果一个节点负责的数据只有一个区间,range based与没有虚拟节点概念的一致性hash很类似;如果一个节点负责多个区间,range based与有虚拟节点概念的一致性hash很类似。
分片特征值的选择,对数据的分片是基于关键值、特征值的。选择特征值需要基于最常用的访问模式,即通过什么访问的就是通过什么当特征值。有时候还需要用到“联合特征值”等多重属性来当特征值。主要还是要根据系统的设计来选择。
元数据服务器,记录数据与节点的映射关系、节点状态等等元数据的服务器。如master、namenode等。元数据服务器需要达到高性能,高可用两个目标,以应对元数据的增长。元数据需要有多个备份,并且能够在故障的时候迅速切换。这时候就要保证数据的一致性了。可以使用主从同步,以主服务器的日志为标准;也可以使用分布式一致性算法如PBFT或Paxos协议或Raft协议等等来实现强一致性了。
为了缓解每次数据请求对元数据服务器的压力,一般会在访问节点上做缓存,这时候就要确保缓存的元数据与元数据服务器上的元数据是一致的,这里使用到的常见技术是使用版本号,即请求的时候带上版本号,路由到具体存储数据的节点的时候,比较版本号,如果不一致就要从元数据服务器中重新拉取元数据并缓存。
还有一种保证缓存强一致的方法就是lease机制,其实就是当元数据服务器发给缓存节点数据的时候附带一个lease,这个lease包含一个有限期,服务器保证在这个有限期内数据不会发生变化,如果服务器在lease期间遇到外部修改请求就会阻塞这个修改,直到lease过期后才会修改数据,并将新的数据和lease发送给节点。此机制需要克服一个服务器和节点时间不一致的问题,服务器时间过慢会使lease发挥不了作用,而过快会有潜藏的危险,工程中一般会将服务器的过期时间设置得比节点得到的过期时间略大来解决问题,当然为了保持一致性最好的方法就是使用NTP(Network Time Protocol)来保证时间同步。
上面说了数据分片,为了达到动态均衡,进行数据的迁移是必要的,如何保证在迁移的过程中保持对外提供服务,这也是一个需要精心设计的复杂问题。
可用性
可用性(Availability)是系统不间断对外提供服务的能力,可用性是一个度的问题,最高目标就是7 * 24,即永远在线。但事实上做不到的,一般是用几个9来衡量系统的可用性,也就是如果要达到4个9的可用度(99.99%),那么一年之中只能有52.6分钟不可用,这是个巨大的挑战。
之所以说要考虑可用性是因为分布式系统故障概率非常高,所以分布式系统其中一个设计目标就是容错,在一定的故障情况下,分布式系统还可以提供服务,这就是系统的可用性。
冗余可以提高分布式系统的可用性。就是说多个节点负责相同的任务,这在分布式存储中使用非常广泛,维护同一份数据的多个节点称之为多个副本。我们考虑一个问题,当向这个副本集写入数据的时候,怎么保证并发情况下数据的一致性,是否有一个节点有决定更新的顺序,这就是中心化、去中心话副本协议的区别。
中心化副本协议中分为同步模式和异步模式,所谓同步(Synchronous replication),就是说对于客户端请求,系统阻塞到复制集中所有节点都更新完成,才能向客户端返回,即write all。而异步(Asynchronous replication)模式,只要一个或者部分节点更新则算写入操作成功,通常是write one。两种模式各有各的优劣,在数据同步的时候选择同步模式还是异步模式呢,这个取决于系统对一致性、可用性、响应延迟的要求。
中心化副本协议的数据流向分为链式和主从模式,链式就是指从一个节点推送到最近的节点,比如GFS,“最近” 可以用IP地址或者节点间心跳TTL来衡量,优点是充分利用网络带宽,减轻primary压力,但缺点是写入延迟会大一些。主从模式则是指数据同时从primary节点到secondary节点,在主从模式下,Secondary会从Primary拉取OPLOG并应用到本地。显然,在这种模式下Primary节点的带宽压力比较大,但是写入延迟会小一些。
理论上,副本集中的多个节点的数据应该保持一致,因此多个数据的写入理论上应该是一个事务:要么都发生,要么都不发生。但是分布式事务(如2pc)是一个复杂的、低效的过程,因此副本集的更新一般都是best effort 1pc,如果失败,则重试,或者告诉应用自行处理。
而选举出primary节点的方法也有两种,一种是由内部节点自行投票,一种是通过其它组件来任命产生。
一致性
从上面可以看到,为了高可用性,引入了冗余(副本)机制,而副本机制就带来了一致性问题。当然,如果没有冗余机制,或者不是数据(状态)的冗余,那么不会出现一致性问题,比如MapReduce。一致性与可用性在分布式系统中的关系,已经有足够的研究,形成了CAP理论。CAP定理就是说分布式数据存储,最多只能同时满足一致性(C,Consistency)、可用性(A, Availability)、分区容错性(P,Partition Tolerance)中的两者。
一致性从系统的角度和用户的角度有不同的等级。系统角度的一致性有:强一致性、弱一致性、最终一致性。用户角度的一致性有单调读一致性,单调写一致性,读后写一致性,写后读一致性。
这里顺便讲一个之前在链闻上看到的关于终结性的概念(来自:一文理解区块链共识机制的终结性),这是关于区块链的一个概念,终结性「finality」是指一旦提交到区块链,所有格式正确的区块都不会被撤销。当用户进行交易时,他们希望一旦其交易通过,交易就不会被任意更改或回滚。因此,在设计区块链共识协议时,终结性就显得至关重要。终结性可分为概率终结性,即交易不被回滚的概率很大,在比特币区块链上,一个交易其实要等到六个区块后才确认其真实性,这就保证了回滚可能性很低;还可以分为绝对终结性,是指基于实用拜占庭容错 PBFT 的协议提供的终结性,交易一旦包含在区块中并添加到区块链上,就会立即被认为已经最终完成;还有一种经济终结性,这种概念中,一个区块的回滚成本会非常高昂。
虽然看起来绝对终结性比概率上的终结性更可取,但是由于存在CAP定理,在进行挑选时,仍然存在一些基本的权衡。由于在分区的情况下系统必须满足分区容错性,那么区块链要么保留一致性,要呢保留可用性。保留一致性的系统宁可中止,也不会允许不准确的交易通过。而保留可用性的系统会允许不准确的交易通过,自身也会继续存在。偏好一致性的系统提供拜占庭容错终结性,而偏好可用性的系统提供概率上的终结性。,
高性能
分布式系统的理想目标是任务与节点按一定的比例线性增长。这就需要系统满足高并发、高吞吐量、低延迟的指标,但是不同的系统关注的核心指标不一样,比如MapReduce,本身就是离线计算,无需低延迟。要满足这三大指标可行的办法有:单个节点的scaleup、分片(partition)、缓存:比如元数据、短事务等等。
一个简化的架构图
概念与实现
负载均衡:
Nginx:高性能、高并发的web服务器;功能包括负载均衡、反向代理、静态内容缓存、访问控制;工作在应用层
LVS: Linux virtual server,基于集群技术和Linux操作系统实现一个高性能、高可用的服务器;工作在网络层
webserver:
Java:Tomcat,Apache,Jboss
Python:gunicorn、uwsgi、twisted、webpy、tornado
service:
SOA、微服务、spring boot,django
容器:
docker,kubernetes
cache:
memcache、redis等
协调中心:
zookeeper、etcd等
zookeeper使用了Paxos协议Paxos是强一致性,高可用的去中心化分布式。zookeeper的使用场景非常广泛,之后细讲。
rpc框架:
grpc、dubbo、brpc
dubbo是阿里开源的Java语言开发的高性能RPC框架,在阿里系的诸多架构中,都使用了dubbo + spring boot
消息队列:
kafka、rabbitMQ、rocketMQ、QSP
消息队列的应用场景:异步处理、应用解耦、流量削锋和消息通讯
实时数据平台:
storm、akka
离线数据平台:
hadoop、spark
PS: apark、akka、kafka都是scala语言写的,看到这个语言还是很牛逼的
dbproxy:
cobar也是阿里开源的,在阿里系中使用也非常广泛,是关系型数据库的sharding + replica 代理
db:
mysql、oracle、MongoDB、HBase
搜索:
elasticsearch、solr
日志:
rsyslog、elk、flume