《数据密集型应用系统设计》笔记-5-分布式系统的挑战

本文深入探讨了分布式系统中常见的问题,包括部分失效、不可靠的网络和时钟。介绍了云计算与高性能计算在应对故障上的区别,强调了分布式系统对高可用性的需求。此外,还讨论了网络错误、不可靠的时钟以及进程暂停带来的影响,并分析了在设计分布式系统时如何处理这些挑战。最后,提到了知识、真相与谎言在系统设计中的重要性,以及面对拜占庭故障和理论系统模型时的思考。
摘要由CSDN通过智能技术生成

前言

这一章的内容是探讨分布式系统普遍存在的一些疑难问题。

故障与部分失效

单节点系统总是只呈现出两种状态:工作或者出错,它通常不会出现模棱两可的现象,相同的操作通常产生相同的结果(确定性)。
分布式系统中,可能会出现系统的一部分工作正常,其他部分出现难以预测的故障,称之为"部分失效"。这种部分失效是不确定的,因此大大提高了分布式系统的复杂性。

云计算和超算

构建大规模计算系统有以下几种思路:

  • 高性能计算high-performance computing HPC.包含成千上万个CPU的超级计算机构成一个庞大的集群。通常用于计算密集型的科学计算任务,如天气预报或模拟原子和分子的运动
  • 云计算:多租户数据中心,通用计算机,用IP以太网连接等等特征
  • 传统企业数据中心位于以上2个极端之间

不同的集群方式所对应的错误处理方法不同。
HPC通常会定期对任务状态进行快照保存。当节点出现故障,解决方案是局部失效上升为整体失效,等节点修复后继续运行任务。HPC错误处理方式类似单节点系统。
本书重点是基于互联网的服务系统,它与HPC有不同之处:

  • 高可用,需要保证系统时时刻刻都可用
  • HPC的节点可靠性高(专用硬件,价格高)。云计算可靠性低
  • 云计算的网络通常基于IP和以太网,采用Clos拓扑结果提供等分带宽。HPC则使用特定的网络拓扑结构(多维网格或toruses)。
  • 系统越大,系统存在组件失效的概率就越大。
  • 如果系统可以容忍某些失效的节点,而整体继续工作,则对系统运维帮助极大。
  • 对于全球分散部署的多数据中心,网络更加不可靠

要使分布式系统可靠工作,就必然面临部分失效。要在不可靠的组件上构建可靠的系统。需要人为考虑各种可能的出错状况。

不可靠的网络

基于不可靠的组件构建可靠的系统案例:

  • 纠错码可以再各种通信链路上准确传输数据
  • IP层并不可靠,但TCP在IP之上提供了更可靠的传输层。

人为错误(配置错误等)是网络产生故障的主要原因。
类似于交通堵塞,计算机网络上数据包延的变化根源往往在于排队。传统的固化网络采用端待端的一条电路链接(电路交换:适用于传输固定流量大小的数据),它不存在排队所以非常可靠,延迟稳定,本质上它是同步网络。而现在的数据中心网络属于异步网络(分组交换:适用于传输无法事先确定流量大小的数据)。

不可靠的时钟

网络上每台设备都有自己的时钟硬件设备,通常是石英晶体振荡器。同步不同机器的时钟,最常用的方法是网络时间协议(Network Time Protocol,NTP),它根据一组专门的时间服务器来调整本地时间,时间服务器则从精确更高的时间源(如GPS接收机)获取高精度时间。

墙上时钟(wall-clock time)/单调时钟

计算机内部至少有两种不同的时钟:wall-clock time单调时钟,本质上它们服务于不同的目的。
单调时钟
单调时钟更适合测量持续时间段(时间间隔),例如超时时间。Linux的clock_gettime(CLOCK_MONOTONIC)和Java System.nanoTime()返回的就是单调时钟。单调时钟的名字来源于它们保证总是向前(不会出现因同步时间服务器而出现回拨)。单调时间的绝对值通常没有意义。
wall-clock time
wall-clock time根据某个日历(也叫墙上时间)返回当前的日期与时间,例如Linux的clock_gettime(CLOCK_REALTIME)和Java System.currentTimeMillis()。墙上时钟可与NTP同步,可能产生回拨现象。
分布式系统的一种冲突解决方案:LWW,最后写入获胜。LWW高度依赖时钟。如果使用物理时钟(单调时钟或墙上时钟),就要求时钟源精度远远高于被测量对象(网络延迟)。对于LWW的排序问题,基于递增计数器这类逻辑时钟是更可靠的方式。

google时钟使用案例-跨数据中心快照隔离:
系统的时钟读数并不是完全精确,它可以视为带有置信度的一个置信区间。一般的系统不会返回这种置信区间。google Spanner的TrueTime API会返回置信区间[不早于,不晚于]代表误差的最大偏差范围。
快照隔离需要生成单调的事务ID。如果事务A的发生时间为 [ A 0 , A 1 ] [A_0,A_1] [A0,A1],事务B的发生时间为 [ B 0 , B 1 ] [B_0,B_1] [B0,B1]。如果两个置信区间有重叠,那么A与B的顺序就无法明确。google Spanner实现方式是:根据TrueTime返回的置信区间,为了确保事务时间戳反映因果关系,Spanner在提交读写事务之前故意等待置信区间长度,这样一来,事务发生的时间绝对不会有重叠。
Spanner这样带来的挑战是需要尽量小的时钟误差,为此Google在每个数据中心都部署一个GPS接收器或原子钟,保证时钟同步在7ms内完成。
原子钟:铯原子的电子在两个能级之间跳跃时辐射的电磁波频率是一定的,利用这个频率作为标准时间,校准时钟振荡器。

进程暂停

在分布式系统中危险使用时钟的例子:假设使用主从复制数据库,那么如何确信主节点可用呢?一种思路是主节点从其他节点获得一个租约(lease),某时间点只有一个节点可以拿到lease。在lease到期之前,该节点就是主节点。为了维持主节点的身份,节点必须在到期之前定期更新租约,现在就产生了问题:

  1. 检查租约是否到期
  2. 如果达到临界值更新租约。

如在第1步之后,结果为租约未过期,此时进程暂停。那么此节点会在进程恢复以后仍然认为自己是主节点。
问题的原因是该进程有可能暂停,原因有多种:

  • 编程语言如JAVA的垃圾回收器GC。GC有时候会暂停所有正在运行的线程,甚至持续数分钟。
  • 在虚拟化环境中,可能会暂停虚拟机。
  • 当机器负载很高时,线程上下文切换产生问题,被暂停的线程可能需要一段时间之后才能再次运行。
  • 如果应用程序执行同步磁盘操作,则线程可能暂停并等待磁盘I/O完成。即使代码没有明确执行IO操作,也难以避免引入磁盘IO。如Java类加载器加载类文件。
  • 如果操作系统配置了基于磁盘的内存交换分区,内存访问可能触发缺页中断,进而需要从磁盘中加载内存页。极端情况下,操作系统可能花费大量时间在页面换入换出,而实际工作完成很少(颠簸).为避免此类问题,通常在服务器上禁用分页,这样在内存紧张时会杀死一些进程释放内存,但不会造成服务器所有业务缓慢。
  • 通过发送SIGSTOP信号暂停UNIX进程,直到进程收到信号SIGCONT之后才会从停止的地方继续运行。

响应时间保证
在许多编程语言和操作系统中,线程和进程可能会暂停相当长的时间。试想一下如下运行环境:飞机,火箭,机器人,汽车。对于这类系统,软件必须有作出响应的上限,如果无法满足,会导致系统级故障,这就是硬实时系统

知识,真想与谎言

分布式系统不使用共享内存交换信息,而是通过不可靠的网络传递消息。如何确定从系统获得的信息哪些是可信的?方法是对系统行为作出若干假设,只要满足这些假设条件,即可认定系统行为正确

真相由多数决定

问题:1.假设某节点的外部网络出现问题,它能接受消息,但不能发送消息,该节点会认为自己运行良好。2.某节点长时间运行垃圾回收,因此被其他节点认定为失效,但该节点本身并不认为自己失效。
以上两种情况说明:节点不能根据自己的信息判断自身的状态。因此分布式系统不能完全依赖于单个节点。目前,许多分布式算法都依靠法定票数,即在节点间进行投票。法定票数必须大于 n / 2 n/2 n/2(节点数 n n n),这样避免出现两个冲突的决定。

主节点与锁

有很多情况,需要在系统范围内只能有一个实例(只允许一个事务/客户端持有特定资源,处理特定业务等)。
现有系统组件:锁服务(如ZooKeeper),客户端A,客户端B,存储。

  1. A从锁服务申请lease0,但发生了"进程暂停"问题。
  2. lease0到期
  3. B从锁服务申请lease0,执行完整个任务
  4. A进程恢复,继续执行任务。与B发生冲突

这里面问题的根源是客户端A错误地认为lease0未到期(或者已经运行完成租约检查)而选择继续执行任务。
Fencing令牌
当使用锁和租约机制来保护资源的并发访问时,必须确保过期的"唯一的"节点不能影响其他正常部分。要实现这一目标可以采用Fencing令牌。在获得租约时同时返回一个单调递增fencing令牌,要求客户端在发送写请求时必须包含fencing令牌。
重新审视该过程:

  1. A从锁服务申请lease0(+fencing0),但发生了"进程暂停"问题。
  2. lease0到期
  3. B从锁服务申请lease0(+fencing1),执行完整个任务
  4. A进程恢复,继续执行任务,但写请求令牌为fencing0<fencing1,被丢弃。

当使用ZooKeeper作为锁服务时,可以用事务标识zxid或节点版本cversion来充当fencing令牌。

只靠客户端自己检查锁状态是不够的,必须有额外的检查机制,服务器检查令牌是推荐的正确做法:系统服务不能假定所有的客户端都表现符合预期。

拜占庭故障

本书总是假设节点虽然不可靠但一定是诚实的,节点发出的信息代表了其所知的真相。如果节点经常发出错误信息,那么如何达成共识呢?拜占庭故障即要在不信任的环境中达成共识的问题。航空航天领域CPU或内存的数据可能会被辐射而发生故障,飞行控制系统必须做到容忍拜占庭故障。解决拜占庭故障异常复杂,一般领域可以忽略。

理论系统模型与现实

为了能够分析分布式系统的各种问题,首先需要对理论系统模型进行归类。每一类系统模型有其独有的特征和问题。
关于计时方面,有三种常见的系统模型:
同步模型:同步模型假定有上界的网络延迟,有上界的进程暂停和有上界的时钟误差。有上界可以保证网络延迟、时钟漂移等不会超过某个固定的上限。
部分同步模型:是指系统大多数情况像一个同步系统运行,但系统延迟有时候会超出预期。这是现实模型。
异步模型:对系统计时不做任何假设。很少见,非常难以处理,只有少数算法可以支持纯异步模型。

节点失效模型也有三种:
崩溃-中止模型:此模型假设一个节点只能以一种方式发生故障–系统崩溃。节点崩溃后无法自行恢复,可视为已消失。这种模型较简单,容易处理。
崩溃-恢复模型:此模型假设节点发生崩溃以后,可能可以自行恢复正常。这是现实的选择。
拜占庭(任意)失效模型:此模型假设节点可能发生任意事情,即没有假设。只有在要求非常极端的场合使用此模型,如航空航天领域。这种模型极难处理。

中国目前的社会形态就是没有假设的,人与人缺乏信任,缺乏共识,行事困难,所以普通人生活是十分消耗精力的。

算法正确性

可以通过描述算法结果的属性定义算法的正确性。其中算法的属性有两种特征:安全性活性。如fencing令牌生成算法必须具有以下属性:

  • 唯一性:两个令牌请求不能获得相同的值。(安全性)
  • 单调递增: t 1 : f 1 , t 2 : f 2 t_1:f_1,t_2:f_2 t1:f1,t2:f2,若 t 1 &lt; t 2 t_1&lt;t_2 t1<t2,则 f 1 &lt; f 2 f_1&lt;f_2 f1<f2 (安全性)
  • 可用性:请求节点不崩溃则最终一定收到响应 (活性)

区分安全性和活性有助于简化处理分布式系统模型。
违反安全性:违规行为无法撤销,破坏实际已发生。
违反活性:活性可以设定一些必要条件,不必要求所有节点都具有活性,只需多数节点没有崩溃即可。系统在任何时刻任何情况都不能违反安全属性,即确保算法不会返回具有破坏性的错误的结果。

总结

本章主要探讨了分布式系统普遍存在的问题,先理解这些问题,才能更好地学习分布式算法。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值