分布式系统的抽象和实现工具
分布式系统无外乎考虑以下三点:
- 存储:构建一个多副本、容错的、高性能的分布式存储实现。
- 通信:在不可靠的且带宽有限的网络上交换数据。
- 计算:通过尽可能并行化任务,降低延迟并提高吞吐率。
同时,分布式系统存的实现目标同样有三点:
- 可扩展性
- 可用性
- 一致性
可扩展性
在最理想情况下,加速效果和系统的计算核芯是线性的,比如:两颗核芯会比一颗核芯快一倍。然而,在操作系统课上我们已经知道任务之间存在 happen-before 关系。这导致有些任务天然可扩展,典型的例子是神经网络的推理,而另一些任务往往难以扩展,例如关系型数据库。
所以,有关扩展性是这样:我们希望可以通过增加机器的方式来实现扩展,但是现实中这很难实现,需要一些架构设计来将这个可扩展性无限推进下去。
可用性(容错)
尽管现代计算机十分可靠,可以预期一台服务器能够正常工作数年,但大型分布式系统往往存在成千上万台服务器,这些服务器通过复杂的网络连接,所以任何一台服务器都可能发生故障。同时,一些很罕见的问题也会被放大,要么执行错误,要么执行缓慢,或者因为各种原因与网络断开连接,导致整个系统暂时失效。
以上问题,使得分布式系统在设计之初需要考虑容错性,即使出现一些错误也应该能够正常运行。常见的实现方式是冗余,即设计多个副本来实现容错。
如果可用性难以实现,还有一个更弱的要求:自我恢复性(recoverability),即系统出错并停止运行后,由人工手动介入修复,接着系统便可继续运行,就好像没有发生过错误一样。
为了实现自我恢复性,常见手段有两种:
- 非易失性存储(常见的就是硬盘):当机器掉电时,需要能够从硬盘中的一些log或checkpoints恢复数据。
- 复制:每个数据项都有多个副本,每个副本都存储在不同的机器上。当一台机器发生故障时,其他机器可以继续运行。但这也带来了新的挑战——一致性。
一致性
从性能和容错的角度来说,我们通常会有多个副本。这导致一致性是分布式系统的一个难点,因为它需要保证所有节点看到的数据是相同的。然而,现实是多个副本意味着会发送各种问题:数据在传输时目标节点下线了,数据传输延迟了,数据传输出错了,等等。
实际上,对于一致性有很多不同的定义。有一些非常直观,比如说get请求可以得到最近一次完成的put请求写入的值。这种一般也被称为强一致(Strong Consistency),但是实现这一点的代价非常高。几乎可以确定的是,分布式系统的各个组件需要做大量的通信,才能实现强一致性。
正是由于强一致性的代价往往很高,所以我们通常不会选择强一致性。相反,我们通常会选择弱一致性,即只需要保证最终一致性即可。最终一致性意味着系统中的数据可能会暂时不一致,但最终会达到一致状态。弱一致性通过牺牲部分的一致性要求,换来了系统效率的提升。
以上要求,产生了分布式系统的“不可能三角”,即CAP定理:
- C(Consistency,一致性):所有节点在同一时刻看到的数据是相同的。
- A(Availability,可用性):每次请求都能得到一个非错误的响应。
- P(Partition tolerance,分区容错性):系统可以在任意网络分区或节点间通信中断时继续运行。
以上三点,只能满足两点,同时牺牲剩下一点的性能。