分布式系统基础理论(一)
个人而言也接触了相当一段时间的分布式系统,这次打算从头梳理一遍分布式系统中比较重要的概念。本文也是参考刘杰先生写的《分布式系统原理介绍》一文作为本次分布式系统系列的文章的核心思路。如有版权问题,请联系我删除,谢谢。
其实在最开始学习分布式系统之前,我们需要思考以下两个问题(按顺序思考):
- 什么是分布式系统
- 分布式解决什么问题?
- 如何描述分布式系统的问题?
说明
本文限定在分布式系统不考虑拜占庭问题,即所有节点是可信的。
什么是分布式系统
分布式系统是若干独立计算机的集合,这些计算机对于用户来说就像是单个相关系统。但是对外部来说,是感觉不到这些主机的存在。也就是说,我们只看到是一个系统在运作。
从进程角度看,两个程序分别运行在两台主机的进程上,它们相互协作最终完成同一个服务,那么理论上这两个程序所组成的系统,也称作是分布式系统。
总结一下就是狭义的分布式系统指由网络连接的计算机系统,每个节点独立地承担计算或存储任务,节点间通过网络协同工作。广义的分布式系统是一个相对的概念,就像Leslie Lamport老爷子所说:What is a distributed system.Distribution is in the eye of beholder.To the user sitting at the keyboard,his IBM personal computer is a nondistributed system.To a flea crawling around on the circuit board,or to the engineer who designed it,it’s very much a distributed system.
分布式主要解决的问题
- 解决SPOF(single point of failure)问题,满足高可用性需求
- 解决Scale out问题,满足扩展性需求
- 解决数据分布问题,满足业务的需求
一般情况,人们为了解决一个问题,往往会引入一个新的问题。试想如下:由于SPOF存在,再加入一个节点作为备份。这样确实提高了系统高可用性,但是又会引入新问题:
- 如何检测节点状态?
- 如果检测主节点失败,备节点如何进行切换?
- 主备节点如何同步数据?
- 网络分化出现双主,如何避免与处理?
分布式系统就是这样,按下葫芦浮起瓢,我们要尽可能做到平衡或者根据业务的不同形式有所侧重。
根据上面列出的4点问题,我再举个例子。
当我们在生产线上用一台服务器来提供数据服务的时候,我们会遇到如下的两个问题:
- 一台服务器的性能不足以提供足够的能力服务于所有的网络请求。
- 我们总是担心这台服务器宕机,这就会造成服务不可用或者数据丢失。
于是我们必须对我们的服务器进行扩展,加入更多的机器来分担性能上的问题,以及来解决单点故障问题。通常我们会通过两种手段来扩展我们的数据服务:
- 数据分区:就是把数据分块放在不同的服务器上(uid%16,一致性哈希等)
- 数据镜像:让所有的服务器都有相同的数据,提供相当的服务
对于“数据分区”情况,我们无法解决数据丢失的问题,单台服务器出问题时,会有部分数据丢失。所以数据服务的高可用性只能通过“数据镜像”来完成–数据的冗余存储(一般工业级认为比较安全的备份数应该是3份,如HDFS和Dynamo)。但是加入更多的机器,会让我们的数据服务器变得更加复杂,尤其是跨服务器的事务处理,也就是跨服务器的数据一致性。这真的是一个老大难问题,最经典的Use Case:“A账户向B账户汇钱”来做个说明,熟悉RDBMS事务的都知道从账户A到账户B需要6个操作:
- 从A账户中把余额读出来
- 对A账号做减法操作
- 把结果写回A账户中
- 从B账户中把余额读出来
- 对B账户做加法操作
- 把结果写回B账户中
为了数据的一致性,这6件事要么全部完成,要么全都不成功,而且这个操作的过程中,对A、B账户的其他访问必需锁死,所谓锁死就是排除其它的读写操作,不然会发生脏数据的问题,这就是事务。在我们加入了更多的机器后,这个事情会变得复杂起来:
- 在数据分区的方案中:如果A账户和B账户的数据不在同一台服务器上怎么办。我们需要做跨服务器的事务处理。也就是说如果A扣钱成功了,但是B的加钱失败了,我们还是要把A的操作给回滚回去。这在跨机器的情况下,就变得十分复杂。
- 在数据镜像的方案中:A账户和B账户间的汇款操作是可以在一台机器上完成的,但是我们有很多个节点存在A账户和B账户的副本。如果对A账户的汇钱有两个并发操作(要汇给B和C),这两个操作发生在不同的两台服务器上怎么办?也就是说在数据镜像中,在不同的服务器上对同一个数据的写操作怎么保证其一致性,保证数据不冲突呢。
同时,我们必须还要考虑性能问题。如果不考虑性能的话,分布式事务得到保证并不困难,系统性能降下来事务完全可以得到保证。除了考虑性能外,我们还要考虑可用性,也就是说,一台机器没了,数据不丢失,服务可由别的机器继续提供。
因此综上,分布式系统中主要考虑下面的几个情况:
- 容灾:数据不丢失、节点的Failover
- 数据一致性:事务处理(Raft&Paxos)
- 性能:吞吐量、响应时间
要解决数据不丢失,只能通过数据冗余的方法,就算是数据分区,每个区内部也需要进行数据冗余处理。这就是数据副本:当出现某个节点的数据丢失时可以从副本读到,数据副本是分布式系统解决数据丢失故障的唯一方法。所有,通常情况下我们只在数据冗余情况下考虑数据的一致性和性能的问题,总结来说:
- 要想让数据有高可用性,就需要写多份数据。
- 写多份数据的问题就会导致节点间数据一致性问题。
- 数据一致性的问题又会引发性能问题。
对于分布式系统理论,分为以下几个子问题:
- 在什么环境下?
- 有哪些节点参与?
- 通过什么样的共识算法?
- 使用的业务类型?
- 达成什么样的容错要求?
环境
分布式系统中的网络模型:
- 同步网络(synchronous network):这里的同步网络和编程中的同步阻塞io和异步非阻塞io是两个概念。同步网络是指所有节点的时钟漂移有上限;网络的传输时间有上限;所有节点的计算速度一样。这意味着整个网络按照round运行,每个round中任何节点都要执行完本地计算并且可以完成一个任意大小消息的传输。一个发出的消息如果在一个round内(或者说超时时间内)没有到达,那么一定是网络中断造成的,这个消息会丢失,不会延迟到第二个round到达。在现实生活中这种网络很少,同步网络仍然是在计算机科学中是不可缺少的一个模型,在这种模型下可以解决一些问题,比如拜占庭式故障。但是我们在生产系统中每天打交道的网络大多是异步网络。
- 异步网络(asynchornous network):和同步网络相反,节点的时钟漂移无上限,消息的传输延迟无上限,节点计算的速度不可预料。这就是我们日常接触的网络类型。在异步网络中,有些故障非常难解决,比如当你发一个节点一个消息之后几秒钟都没有收到他的应答,有可能这个节点计算非常慢,但是也可能是节点crash down或者网络延迟造成的。很难判断到底是发生了什么样的故障。
系统异常是常态:
机器异常通常有以下几种情况:
- 机器硬件级别故障
- 操作系统故障
- 软件故障
- 资源耗尽,如内存,CPU,硬盘空间,网络带宽
网络传输不可靠:
主要体现在:
- 丢包,传输成功不确定性
- 延时,延时时间不确定性
- 重传与报文重复
- 乱序
并发:
如同操作系统中多线程并发,分布式系统多节点同时并发操作。但是分布式系统处理并发情况不能像多线程上通过操作系统的锁机制来处理并发,在分布式系统实现一个锁比单节点操作系统中难度大很多。
缺少全局时钟:
每个节点都有自己的本地时钟,就像每个人有一只表,但是每个人的表的时间却不一定相同。分布式系统不同节点很难有相同的时钟。
节点
节点数量:节点数量,在实践过程中,至少两个,常见三个节点,部分情况有五个节点
节点角色:以Raft为例,可以分为Leader、Follower、Candidate
节点可信:全部都是可信节点,不存在作恶节点,也就是非拜占庭网络
节点准入:主要方式是通过配置管理指定节点
节点配置:虽然节点之间机器配置,网络带宽,地理位置都会存在一定程度上的差异,但是可以控制
共识算法
共识算法是分布式系统的核心,是解决分布式数据一致性的关键。最常见共识算法:
- Paxos
- Raft
- Zab
- Primary-secondary
- Quorum
在一个分布式系统中,对于propose and decide问题,尽管过程中出现机器crash down,系统仍然可以make final decision。而做出系统中所有节点最终达成一致性意见必须满足以下条件:
-
Agreement:
Every correct process must agree on the same value
当系统做出final decision后,所有存活的机器的final decision应当一致。
-
Validity:
If all the correct processes proposed the same value v,then any correct process must decide v
如果没有宕掉的机器都提出相同的decision v,那么final decision一定是v。
-
Termination
Eventually,every correct process decides some value
所有正常存活的机器都必须提交local decision。
常见业务类型
- 分布式存储,如GFS、HDFS
- 分布式计算,如MR、Spark
- 分布式锁,如Chubby
- 分布式数据库,如BigTable、Spanner
- 分布式ML,如TensorFlow
- 分布式MQ,如Kafka
容错要求
对于分布式系统而言,对于容错要求要根据具体业务来划分。涉及到的理论是CAP定理,这是分布式领域内的经典问题,后面会用一篇文章专门描述CAP问题。
总结
分布式系统基本就是为解决以上问题而生的,在解决这些问题的时候又会衍生出很多问题,最经典最重要的就是一致性算法问题,这也是分布式系统中不可或缺的问题,后面一系列文章会有很大篇幅也会谈到这个问题。