当前位置: 首页 > 科技观察

Spanner、实时和CAP理论

时间:2023-03-15 19:32:48 科技观察

Spanner是Google的高可用性全球关系数据库[CDE+12]。Spanner可以管理大规模的复制数据,这不仅体现在数据量上,还体现在事务数量上。Spanner为每条写入它的数据分配一个全局一致的“真实时间”时间戳,客户端可以在不加锁的情况下对整个数据库进行全局一致的读取。CAP理论[Bre12]指出,在C、A、P的三个预期属性中,只能选择两个:C:一致性,我们在本文中可以认为是可串行化;A:可用性,指读取和更新100%可用;P:networkpartition,指对网络碎片的容忍度。根据CAP理论,选择丢弃哪些字母将导致三个系统:CA、CP和AP。请注意,您不必选择3个属性中的2个,许多系统具有零个或一个CAP属性。对于“全球规模”的分布式系统,分区通常被认为是不可避免的——尽管这不是一个共识[BK14]。一旦你认为分区是不可避免的,那么任何一个分布式系统都必须做好放弃一致性(AP)或者放弃可用性(CP)的准备,这不是任何人想要的选择结果。事实上,CAP理论的出发点就是让设计者认真对待这种权衡。这里给出两个重要的警告:首先,当真正发生分区时,你只需要失去系统的部分功能,而不是全部——当然你必须付出一些代价[Bre12]。其次,CAP理论宣称100%的可用性,而本文讨论的有趣的事情是在实际生产环境中涉及可用性本身的妥协和权衡。Spanner是一个CA系统。Spanner作为一个全局分布式系统,号称同时具备一致性和高可用,也就是说Spanner系统不会分区,这是非常值得怀疑的。这是否意味着Spanner就是CAP定义的CA系统?从技术上来说,直接的答案是“否”,但从效果上来说,答案可以是“是”——也就是说,从用户的角度来看,Spanner可以认为是一个CA系统。从纯粹主义者的技术角度来看,答案当然是否定的。因为分区确实发生过,而且在谷歌确实发生过。特别是当分区发生时,Spanner的策略是选择C,放弃A。所以,从技术上讲,Spanner是一个CP系统。接下来,我们将详细讨论分区。考虑到Spanner将始终提供一致性,Spanner声称CA系统的真正问题是用户对可用性的态度——即用户是否会发现可用性的错误。如果Spanner的实际可用性非常高,以至于停机事件对用户来说可以忽略不计,那么Spanner就可以证明“实际上是一个CA系统”的说法。声称CA系统并不意味着100%的可用性(Spanner不提供也不会提供),而是类似于5或??更多“9”可用性(1/106故障或更少)。反过来,真正考验数据库可用性的试金石是用户(谁希望他们的服务高可用)——即用户是否需要编写额外的代码来处理停机异常。如果用户不写额外的代码,也就是说用户认为数据库是高可用的。基于Spanner大量的现有用户,我们可以知道用户认为Spanner是一个高可用的数据库。可用性数据在我们深入研究Spanner之前,有必要回顾一下Chubby的演变。Chubby也是一个提供一致性和可用性的全球范围(广域网)系统。最初的Chubby论文[Bur06]提到在700天内发生了9次30秒或更长时间的中断,其中6次与网络相关(如[BK14]中所讨论)。这样的可用性低于最好的5个9的可用性,实际上,如果我们假设每次停机的平均停机时间为10分钟,这样的可用性低于4个9,更糟糕的是,如果停机时间为每小时,则可用性将低于3个9。?对于锁定和一致的读/写操作,由于在网络、架构和运维方面的各种改进,分布在世界各地的Chubby单元现在提供了99.99958%的平均可用性(30s+停机时间)。从2009年开始,由于“出色”的可用性,Chubby的SRE工程师开始强制执行定期停机,以确保我们继续了解Chubby对故障的依赖和影响。在谷歌内部,Spanner提供了与Chubby类似的可用性。也就是说,优于五个九。云版Spanner基本相同,但增加了一些新功能,因此在实际生产环境中可能会略低一些。上面的饼图揭示了扳手事故的内部原因。事故可能是突发事件,但并非所有事故都是停机,有些事故很容易被掩盖。图中的权重是事件发生的频率,而不是事件的影响。最常见的事件类别(用户)是由用户错误引起的,例如高负载或配置错误,并且这些事件中的大多数只会影响该特定用户,而其余类别可能会影响该区域内的所有用户。集群类别中的事件反映了底层基础设施中的非网络问题,包括服务器和电源问题。Spanner通过使用多个副本自动规避这些事件。然而,有时SRE也需要介入修复损坏的副本。Operator类别中的事件是由SRE引起的事件,例如配置错误。Bug类别中的事件是导致某些问题的软件错误。这些事件中的每一个都可能导致重大或轻微的中断,有史以来最大的两次中断是同时影响特定数据库的所有副本的软件错误。大多数其他麻袋的各种问题只发生一次。网络类别中的事件(低于8%)是由网络分区和网络配置问题引起的。从来没有出现过部分集群节点与其他集群节点分离的情况,也从未出现过Spanner的quorum节点出现在分区中集群较少的一侧。但我们确实有个别数据中心或区域与其他网络断开连接的情况。还有一些错误的配置导致临时保留的带宽不足和一些与硬件故障相关的延迟。我们曾经遇到一个问题,一个方向的通信失败导致出现奇怪的分区,然后不得不通过关闭一些节点来解决这个问题。到目前为止,网络事件尚未造成重大停机。综上所述,对于一个系统要被声明为“实际上是一个CA”,那么,系统必须处于这种相对概率状态:1)系统必须在生产中具有非常高的可用性(以便用户可以忽略异常),以及2)系统一定处于相对概率状态,由于分区导致的宕机故障次数应该处于比较低的水平。Spanner满足了两者。网络是根许多人认为,Spanner可以通过使用TrueTime(真实时间)来绕过CAP理论。TrueTime(真实时间)是一种可以使用全球同步时钟的服务。虽然TrueTime很棒,但它对实现CA系统贡献不大,下面将讨论它的实际价值。如果Spanner有什么特别之处,那就是Google的WAN。基于多年的运营改进,可以通过最大限度地减少生产环境中分区的发生来实现高可用性。首先,谷歌的系统运行在自己的专用全球网络上。Spanner不在公共互联网上运行——事实上,Spanner的每个数据包都只流经谷歌控制的路由器和链接(不包括到远程客户端的边缘链接)。此外,每个数据中心通常至少有三个独立的光纤将其连接到私有全球网络,以确保每对数据中心的路径多样性(pathdiversity)。同样,数据中心内的设备和路径也存在冗余。因此,通常灾难性事件,例如切断光缆,不会导致分区或停机。因此,分区的真正风险不是网络路径断开,而是某种类型的广泛配置或软件升级同时切断多条网络路径。这是真正的风险,谷歌正在努力预防和减轻它。一般策略是限制任何特定更新的范围(“爆炸半径”),以便当我们需要不可避免地推送错误更改时,只有部分路径或副本受到影响,然后我们必须先修复这些变化。虽然谷歌的底层网络可以大大降低分区的风险,但它无法提高光速。跨WAN的一致操作具有显着的最短往返时间,可能为数十毫秒,跨越大陆时则更长(1000英里约为500万英尺,每纳秒?英尺,因此至少为10毫秒)。为了平衡区域内延迟和灾难恢复的需求,Google定义了一个往返时间仅为2ms的“区域”。Spanner通过事务流水线来减少延迟,但这并没有减少单事务延迟。对于读取操作,由于能够使用全局时间戳和本地副本(如下所述),延迟通常较低。弱一致性的模型具有较低的更新延迟。然而,由于没有长距离往返,弱一致性模型的持久性窗口也较低,因为在将数据复制到另一个区域之前,灾难可能会破坏本地和远程副本。网络分区期间发生了什么为了理解分区,我们需要更多地了解Spanner的工作原理。与大多数ACID数据库一样,Spanner使用两阶段提交(2PC)和严格的两阶段锁定来确保隔离和强一致性。2PC被称为“反可用性”协议[Hel16],因为2PC要求所有成员节点都必须参与工作(即必须可用)。为了缓解这个问题,Spanner为每个2PC成员节点建立了一个Paxos组,这样即使Paxos组中的部分成员宕机,2PC的“成员”仍然是高可用的。同时,数据也被分组,形成放置和复制的基本单位。上文提到,一般情况下,当分区发生时,Spanner会选择C??并丢弃A。在实际生产环境中,这是基于以下考虑:使用Paxosgroups来实现更新的共识。如果领导节点由于分区而无法维持法定人数(quorum),则更新停止,系统变得不可用(由CAP定义)。最终,在多数节点的划分中,将重新选举出新的领导节点。使用2PC进行跨组事务意味着分区成员可以阻止事务提交。生产环境分区最有可能的结果是选举出新的leader节点后,quorum节点的分区继续工作,即服务继续可用,但少数节点分区一侧的用户无法访问服务。这是一种差异可用性的情况:节点数量较少的分区一侧的用户可能会遇到其他主要问题,例如失去连接,或者已经宕机。这意味着构建在Spanner之上的多区域服务即使在分区期间也能正常工作。也有可能,但不太可能,某些Paxos组将完全不可用。只要所有关联的组都有法定人数选举的领导者并且都在分区的一侧,Spanner中的“事务”就会工作。这意味着有些事务会完美运行,有些会超时,但系统将始终保持一致。Spanner的一个实现属性是任何读取都会始终如一地返回,即使事务稍后中止(包括出于任何原因,例如超时)。除了常规事务,Spanner还支持快照读取——从过去的特定时间读取。Spanner随着时间的推移维护数据的多个版本,每个版本都有一个时间戳,因此可以使用正确的版本准确地回答快照读取。特别是,每个副本都知道它何时被写入(必需),并且任何副本都可以在该时间戳之前单方面响应读取(除非它太旧并且已被垃圾收集)。同样,同时(异步)读取多个组也很容易。快照读取根本不需要锁。事实上,只读事务被实现为读取当前时间的快照(在任何最新的副本上)。因此,快照读取使分区更加健壮。特别地,快照读取将在以下条件下工作:每个组的至少一个副本存在于正在初始化的分区中,并且这些副本的时间戳已过时。如果leader节点由于分区而失败,并且在分区的同时继续失败,则第二个子句可能不成立,因为不可能在分区的这一侧选举出新的leader节点。在分区期间,在分区开始之前的时间戳读取数据很可能在分区的两边都成功,因为数据的任何可达副本都满足需求(译者注:似乎可用性取决于用户是否需要最新数据)。关于TrueTime(真实时间)一般来说,同步时钟可以避免分布式系统中的通信。BarbaraLiskov通过许多示例[Lis91]提供了详细的概述。出于我们的目的,TrueTime是一个具有有限非零错误的全局同步时钟:它返回一个包含执行调用的实际时间的间隔。因此,如果这两个间隔不重叠,我们就知道调用必须实时排序。如果间隔重叠,我们就无法知道调用的实际顺序。Spanner的精妙之处之一是它从分布式锁中获取序列性,从TrueTime中获取外部一致性(类似于线性化)。Spanner的外部一致性不变性意味着,对于任意两个事务,T1和T2(即使在地球的另一边):如果T2在T1提交之后开始提交,则T2的时间戳大于T1的时间戳。借用Liskov[Lis91,第7节]:使用同步时钟的可能性降低了违反外部一致性的可能性。本质上,主服务器持有整个备份副本集的租约。备份节点发送给主节点的每条消息都会向主节点授予租约。如果主存储持有来自大多数备份节点的未到期租约,则可以单方面读取主存储。...该系统中的一个不变性是,每当主服务器节点执行读取时,它必须持有来自大多数备份节点的有效租约。如果时钟不同步,则此不变量将不成立。Spanner使用TrueTime作为时钟,以确保不变量成立。特别是,在事务提交期间,领导者必须等到提交时间被确认为过去(基于误差范围)。这种“提交等待”在生产环境中的等待时间并不长,并且与(内部)事务通信并行发生。一般来说,外部一致性需要单调递增的时间戳,“等待不确定性”是一种常见的模式。Spanner通过续租来延长leader节点的选举时间,一般为10秒。正如Liskov所讨论的,每当一定数量的节点同意一个决定时,租约就会延长,因为参与节点刚刚验证了领导节点是有效的。当一个leader节点出现故障时,有两种选择:1)等待租约到期然后选举新的leader节点,或者2)重启旧的leader节点,这样可能会更快。对于一些失败,我们可以发出一个“last”的UDP包来释放租约,用来加速租约到期。由于Google数据中心很少发生计划外故障,因此长期租用很有意义(译者注:乐观的做法)。租约还确保领导节点之间的时间单调性,并使组参与节点能够在租约时间内为读取服务(即使没有领导者)。然而,TrueTime的真正价值在于它能够拍摄一致的快照。回过头来看,多版本并发控制(MVCC)[Ree78]的历史由来已久——将旧版本分开,读操作从旧版本读取数据,不考虑当前事务操作。这是一个非常有用但被低估的属性:特别是,Spanner快照是一致的(相对于快照时间而言),因此无论系统保留什么不变量,快照也将被保留。即使您不知道不变量是什么,这也是事实!本质上,快照是在连续事务之间拍摄的,并反映了快照时间之前的所有内容(更多内容即将到来)。如果没有事务一致的快照,系统很难从过去的某个时间点重新启动,因为某些不变量或完整性约束可能已被事务部分提交的内容所违反。正是由于缺乏一致性,有时很难从备份中恢复系统——冲突的数据需要手动恢复。例如,考虑使用MapReduce对数据库执行大型分析查询。如果你使用Bigtable作为你的数据库,虽然Bigtable也存储了过去版本的数据,但是数据分片的时间戳是“参差不齐”的,这使得结果不可预测,有时甚至不一致(尤其是最近的数据)。如果您使用Spanner作为您的数据库,同样的MapReduce操作可以选择精确的时间戳并获得可重复和一致的结果。TrueTime还可以跨多个独立系统记录快照-只需使用(单调递增的)TrueTime时间戳提交,就快照时间达成一致,并随时间存储多个版本(通常在日志中间)。这不仅限于Spanner:您可以制作自己的事务系统,并且快照在两个系统(甚至k-systems)上都是一致的。一般来说,在这些系统上你需要一个2PC(同时持有锁)来同意快照时间并??确认成功,但系统不需要就其他事情达成一致并且可以完全不同。您还可以使用时间戳作为标记来传递工作流。例如,如果对系统进行了更新,您可以将更新后的时间戳传递给工作流的下一阶段,以确定系统是否反映了自该事件以来的时间。在分区的情况下,不一定。在这种情况下,下一阶段实际上应该等待以保持一致性(或继续工作以保持可用性)。没有时间戳令牌,很难知道您需要等待什么。当然,传递时间戳并不是解决这个问题的唯一方法,但它是一种优雅、健壮的方法,同时确保最终的一致性。当不同的阶段不共享代码并且有不同的管理员时,这尤其有用——两者可以在没有沟通的情况下就时间达成一致。快照是关于过去的,Spanner也可以约定未来的时间。Spanner的一项功能是您可以就未来何时进行架构更改达成一致。这允许您暂停将更改应用到新模式,以便能够同时为两个版本提供服务。准备就绪后,您可以选择一个时间点在所有副本上同时自动切换到新模式(您也可以选择以前的时间点,但您将无法在目标时间之前准备好).至少在理论上,您可以在未来采取行动,例如删除或更改可见性计划。TrueTime本身可能会受到分区的阻碍。TrueTime的来源是GPS接收器和原子钟的组合,两者都通过自身的微小漂移保持准确的时间。由于每个数据中心都有一个“主时间服务器”(冗余),分区的双方都可能继续享受准确的时间。但是,各个节点需要通过网络连接到时间服务器,否则它们的时钟会漂移。因此,在分区期间,独立节点之间的间隔根据本地时钟漂移的速率随时间缓慢增长。基于TrueTime的操作,例如Paxos领导者选举或事务提交,因此需要等待更长的时间,但操作仍然可以完成(假设2PC和quorumquorum通信正常)。加州大学伯克利分校谷歌基础设施教授EricBrewer总结说,作为一个全球范围的分布式系统,Spanner只提供了大于5个九的一致性和可用性,但Spanner声称是一个“实际CA”系统是合理的。与Chubby一样,如果您可以控制整个网络(尽管在全球范围内并不常见),那么在真实的生产环境中CA组合是可能的。即便如此,网络路径中的大量冗余、处理相关故障的架构规划以及精心规划的操作和维护仍然是必需的,尤其是在系统升级时。在发生中断的情况下,Spanner会选择一致性而不是可用性。Spanner使用2PC进行序列化,使用TrueTime进行外部一致性、无锁一致性读取和一致性快照。【本文为专栏作家施施原创文章。转载请通过作者微信获得授权公众号Butianys(butianys)】点此阅读作者更多好文