查看原文
其他

高可用简史

ImportNew 2019-10-03

(给ImportNew加星标,提高Java技能)


转自:开源中国

链接:www.oschina.net/translate/brief-history-high-availability


我曾经访问过一个网站,它有“几个小时的运营时间”,只有当它的实体网站亮着灯的时候,它才会“开放”。我感到很困惑:电脑每天都能运行,为什么网站不能每天都开放呢?我已经习惯了互联网不可思议的可用性。


然而,在互联网之前,7*24 小时可用性可不是一件简单的事。我们想要可用性,但不是从根本上认为应该拥有的东西。我们只在需要的时候才使用电脑;那些电脑是不会坐等一个偶然的请求的。随着互联网的发展,那些以前不常见的请求虽然是在当地时间凌晨3点发来,但在全球其他地方却是黄金营业时间,确保一台电脑能够为这些请求提供服务是很重要的。


许多系统只依赖一台计算机来实现这些请求 —— 我们都知道这是一个结局不太好的故事。通常为了保持系统正常运行,我们需要在多台计算机之间分配负载,以满足我们的需求。然而,分布式计算尽管有其众所周知的优点,但也有其尖锐的缺点:特别是同步和冗错系统中的部分故障。每一代工程师都在重复这些解决方案,以满足他们的时间需求。


如何将分布式应用到数据库中是一个特别有趣的问题,因为这是一个比计算机科学的其他领域发展慢得多的难题。当然,可以使用软件在本地数据库中跟踪一些分布式计算的结果,但是数据库本身的状态只会保存在一台机器上。为什么呢?因为跨机器复制状态是很难的。


在这篇文章中,我们来看看分布式数据库在历史上是如何处理系统中的部分故障的,并在高层次上理解高可用性是什么样子的。


使用我们已有的武器:主动-被动


在过去,数据库在单个机器上运行。只有一个节点,它处理所有的读和写。没有所谓的“部分故障”;数据库不是正常就是停机。


在互联网时代的双重问题考验下,单一数据库的方案是完全失效的:首先,计算机是全天候被访问的,因此停机会直接影响用户;其次,将计算机置于持续不断的需求之下,它们更有可能失败。这个问题显然的解决方案是拥有多台能够处理请求的计算机,这就是分布式数据库的真正开始。


生活在单节点世界中,最自然的解决方案是继续让单个节点服务于读写,并将其状态同步到第二台被动机器上 —— 因此,主从备份诞生了。



主从备份通过在活动节点失败的情况下使用最新的备份来提升可用性 —— 您可以轻易地开始将流量定向到从节点,从而将其提升为活动节点。只要有可能,您就会用一台新的被动机器替换掉宕机的服务器(并希望主节点机器在此期间不会出现故障)。



首先,从主节点复制到从节点是一个同步过程,即在从节点确认复制完成之前,不会提交转换。问题来了,如果从节点宕机了该怎么办?如果备份系统不可用,那么整个系统就没有意义了 —— 然而同步复制的确会发生这种情况。



为了进一步提高可用性,数据可以异步复制。虽然它的体系结构看起来相同,但是它能够在不影响数据库可用性的情况下处理主节点或从节点。


虽然异步主从备份是又一个进步,但仍有明显的缺点:


  • 当活动节点宕机时,任何尚未复制到从节点的数据都可能丢失 —— 此时客户端认为数据已完全提交。

  • 通过依赖一台机器来处理流量,您仍然被限制到一台机器的最大可用资源上。


追逐 5 个 9:扩展到多台机器


随着互联网的发展,商业需求的规模和复杂性都在增长。对于数据库来说,这意味着它们需要能够处理比任何单个节点都能处理的更多流量,提供“始终在线”的高可用性成为了一项重要任务。


鉴于许多工程师现在都有从事其他分布式技术工作的经验,很明显,数据库可以超越单节点主从备份设置,在许多机器上分布式运行数据库。


分片


再一次,最容易开始的地方就是调整你现有的设计,所以工程师通过开发分片将主从复制调整为可伸缩的复制。


在这个方案中,您将集群的数据按照某个值(例如行数或主键中的惟一值)进行分割,然后将这些段分布到多个站点中,每个站点都有一个主从复制对。然后,在集群前面添加某种路由技术,将客户机定向到正确的站点以满足其请求。



分片可以让您在许多机器之间分配工作负载,提高吞吐量,并通过容忍更多的分区故障来提供更高的可用性。


分片尽管有很多优点,但实现起来比较复杂,并且给团队带来了巨大的操作负担。经过深思熟虑的分片算法可能会变得非常繁重,以至于路由最终会悄悄进入应用程序的业务逻辑。更糟糕的是,如果您需要修改系统的分片方式(比如更改模式),那么通常需要进行大量的工程工作。


单节点主从复制系统也提供了事务支持(即使不是强一致性)。然而,在各片之间协调事务的困难是如此复杂,许多分片系统决定完全放弃它们。


多活方案


考虑到分片数据库难以管理且功能不全,工程师们开始开发至少能解决其中一个问题的系统。出现的系统仍然不支持事务,但是管理起来非常容易。随着应用程序正常运行时间需求的增加,帮助团队满足 SLA 是一个明智的决定。


这些系统背后的原理是,每个站点可以包含集群数据的一部分(或全部),并为其提供读写服务。每当一个节点收到写操作时,它就会将更改传播到所有需要它的副本的其他节点。为了处理两个节点收到相同键的写操作的情况,在提交之前需将其他节点的转换输入到冲突解决算法中。考虑到每个站点都是“活动的”,它被称为多活方案。



因为每个服务器都可以处理所有数据的读写,所以分片更容易实现算法,部署也更容易管理。


就可用性而言,多活方案非常好。如果某个节点失败,只需将客户机重定向到另一个包含数据的节点。只要数据的一个副本是实时的,您就可以为它提供读写服务。



虽然这个方案在可用性方面非常出色,但是它的设计从根本上没法做到一致性。因为每个站点都可以处理键的写入(在故障转移场景中也是如此),所以在处理数据时保持数据完全同步是非常困难的。相反,这种方法通常是通过冲突解决算法来协调站点之间的冲突,该算法对如何“消除”不一致做出粗粒度的决策。


因为这个解决方案是在事后完成的,在客户端已经收到关于某个过程的响应 —— 并且理论上已经基于响应执行了其他业务逻辑之后 —— 多活复制很容易在数据中生成异常。


但是,考虑到正常运行时间的溢价,停机的成本被认为大于潜在异常的成本,因此多活成为了主要的备份方案。


大规模部署中的一致性问题:一致性和多活可用性


虽然多活方案似乎解决了基础设施面临的主要问题 —— 可用性,但它只是通过放弃事务来实现的,这使得需要强一致性的系统没法选择它。


例如,谷歌的广告业务使用了一个庞大而复杂的 MySQL 分片系统,它严重依赖 SQL 来任意查询数据库。因为这些查询常常依赖于二级索引来提高性能,所以它们必须与它们派生的数据保持完全一致。


最终,系统变得足够大,以至于它开始给使用分片的 MySQL 带来问题,因此他们的工程师开始设想如何能够同时拥有一个可大规模伸缩的系统,同时能够提供他们业务所需的强一致性。多活方案缺乏事务支持,这意味着它不是一个可选项,因此他们不得不设计一些新的东西。他们最终得到的是一个基于共识复制的系统,这将保证一致性,也可以提供高可用性。


使用一致复制,向节点提交写操作,然后复制到一些其他节点。一旦大多数节点确认了写入,就可以进行提交。



一致性与高可用性


这里的关键概念是,一致复制是位于同步复制和异步复制之间的折中方案:需要任意数量的节点来实现同步,但这些节点是什么并不重要。这意味着集群可以容忍少数节点宕机,而不会影响系统的可用性。(对于宕机的机器的流量状况等提出警告)



但是,协商一致的代价是,它需要节点与其他节点通信来执行写操作。虽然可以采取一些步骤来减少节点之间的延迟,例如将节点放在相同的可用性区域中,但是这会在可用性方面发生权衡。例如,如果所有节点都在同一个数据中心中,那么它们之间的通信速度很快,但是您无法在整个数据中心宕机的情况下存活下来。将节点分散到多个数据中心会增加写操作所需的延迟,但可以在整个数据中心离线时不影响应用程序,以此提高可用性。


多活可用性


CockroachDB 实现了从谷歌 Spanner 论文中获得的大部分知识(值得注意的是,它不需要原子钟),其中包括除了一致复制之外的其他特性,这些特性使可用性变得更加简单。为了描述这是如何工作的,并将其与双活区分开来,我们创造了多活可用性这个术语。


双活 vs 多活


双活方案通过让集群中的任何节点为读写提供服务来实现可用性,但是只在提交写操作之后才将它接受的更改传播到其他节点。


而多活方案允许任何节点服务于读和写,但确保大多数副本在写上保持同步(docs),并且只服务于最新版本的副本的读(doc)。


就可用性而言,双活只需要一个副本就可以同时为两个写操作提供服务,而多活方案则需要大多数副本在线以达成一致(这仍然允许系统内部出现部分故障)。


然而,这些数据库的可用性的区别是一致性的差异。双活数据库在大多数情况下都能很好地接受写操作,但是不能保证客户端能够在现在或将来读取数据。另一方面,多活数据库只有在能够保证以后可以以一致的方式读取数据时才接受写操作。


昨天,今天,明天


在过去 30 年里,数据库可用性已经取得了重大进展,现在已支持全球范围的部署,这些部署让我们觉得服务永远不会停机。该领域的首次尝试通过主从复制奠定了重要的基础,但最终,我们需要更好的可用性和更大的规模。


在此基础上,该行业开发了两种占主导地位的数据库范例:双活(Active-Active),用于关注快速写操作的应用程序;多活(Multi-Active),用于需要一致性的应用程序。


我们都能期待有一天可以利用量子纠缠技术,进入管理分布式状态的下一个范式。


推荐阅读

(点击标题可跳转阅读)

RabbitMQ 如何实现对同一个应用的多个节点进行广播

六边形架构 Java 实现

低延迟 Java(1):介绍


看完本文有收获?请转发分享给更多人

关注「ImportNew」,提升Java技能

喜欢就点一下「好看」呗~

    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存