作者 | Sean Loiselle, Jessica Edwards
译者 | 崔莹峰
策划 | 云昭
电脑可以没日没夜地运行,但早先的网站却做不到24*7小时的运营。现在看来我们都不可思议。然而,在互联网出现之前,24*7的高可用性这个提法并不存在。
那时我们对可用性是有期待,但我们却压根不会认为这是我们有权要求获得的东西。我们只在需要时使用计算机;更多的时候我们其实本来就不太有机会使用它,自然也很少会让计算机一直开着以便它可以随时响应我们。随着互联网的发展,那些以前在当地时间凌晨 3 点不常见的请求变得越来越多,也让这个时间段在全球各地都变成了黄金营业时间,因此确保计算机能够服务这些请求变得非常重要。
然而,许多系统仅依靠一台计算机来处理这些请求——单点故障——我们都知道这是个很糟糕的事实。为了保持正常运行,我们需要在多台可以满足我们需求的计算机之间分配负载。然而,分布式计算有着非常明显的优势:特别是同步和容忍系统内的部分故障(容错)。每一代工程师都在迭代这些解决方案,以满足这个时代的需求。
数据库领域是如何引入分布式的,这件事很多人并不清楚前因后果,因为这本身就是一个比其他领域发展缓慢得多的难题。当然,软件在本地数据库中会记录一些分布式计算的结果,但数据库本身的状态只能保存在单台机器上。为什么?因为跨机器的状态复制是非常困难的。
在这篇文章中,我们来看看分布式数据库在历史上是如何处理容错的,以及高可用性是什么样子的。另外,我们还会介绍高可用性系统的几种不同类型。
高可用性数据库有哪些类型?
高可用性数据库一般情况下可分为两类,目前出现了第三类,而且变得越来越普遍:
主-从数据库:数据库有一个主节点处理请求,另外一个是热备节点(即从节点),一旦主节点故障后就会投入使用
主-主数据库:数据库具有多个主节点,这些节点将数据分片后分别对数据库进行写操作
多主数据库:数据库具有至少三个主节点,这些节点都可以对集群中的任何数据执行读写操作而不会产生冲突。
什么是主从可用性?
主从可用性意味着数据库有一个主节点处理请求,另外一个是热备节点(即从节点),一旦主节点故障后就会投入使用。主从可用性模型基于两节点概念,即一个节点接收所有请求,然后再将数据复制到其追随者。
在过去,数据库在单台机器上运行。它只有一个节点,处理所有的读写操作。没有所谓的“部分失败”;数据库不是成功启动就是失败停服。
对互联网来说,单节点数据库的完全不可用是一个双重问题;首先,计算机全天候被访问,因此停机更有可能直接影响用户;其次,通过将计算机置于持续的访问请求之下,它们更有可能出现故障。很明显,这个问题的解决方案是使用多台计算机来共同处理请求,这也是分布式数据库起步的契机所在。
生活在单节点世界中,最自然的解决方案是继续让单个节点提供读写服务,并将其状态简单地同步到辅助的备份机器上——因此,主从复制诞生了。
主从模式是通过即时备份实现高可用性的早期步骤。在主节点发生故障的情况下,您可以简单地将流量引导到从节点,从而将其提升为主节点。只要有可能,您就会用一台新的备份机器替换停机的服务器(并希望主节点在此期间不会出现故障)。
首先,从主节点到从节点的复制是一个同步过程,即在从节点确认之前,数据转换并不会被提交。但是,目前还不清楚如果从节点出现故障该怎么办。如果备份系统不可用,整个系统宕机当然没有意义——但是只要使用同步复制,就会发生这种情况。
为了进一步提高可用性,可以改为异步复制数据。虽然它的架构看起来是一样的,但它能够处理主节点或从节点的宕机,而不会影响数据库的可用性。
虽然异步主从模式是向前迈出的又一步,但仍然存在明显的缺点:
当主节点宕机时,任何尚未复制到从节点的数据都可能丢失——即使客户端已经确认了数据完全提交。
依靠单台机器来处理流量,仍然受限于单台机器的最大可用资源。.
对五个 9 的高可用性追求
扩展到多台机器
随着互联网的普及,业务需求的规模和复杂性都在增长。对于数据库来说,这意味着它们需要能够处理比任何单个节点都多的流量,并且提供“始终在线”的高可用性成为一项任务。
鉴于现在大量工程师拥有从事其他分布式技术的经验,很显然,数据库可以超越单节点的主从模式,将数据库分布在多台机器上。
分片
同样,我们可以从调整现有的系统起步,我们的工程师通过开发分片将主动复制调整为更具可扩展性的架构。
在此方案中,您按某个值(例如行数或主键中的唯一值)拆分集群的数据,并将这些段分布在多个实例,每个实例都有一套主从节点。然后,您在由这些实例组成的集群前添加某种路由技术,以将客户端请求引导到正确的实例来处理。
分片允许您在将工作负载分配到多台机器上,从而提高吞吐量,并通过容忍更多的部分故障和消除单点故障来创建更大的弹性。
尽管有这些好处,但对系统进行分片是复杂的,并且给团队带来了巨大的运维负担。特意对碎片进行的统计可能会变得非常繁琐,以至于路由最终会渗入应用程序的业务逻辑。更糟糕的是,如果您需要修改系统分片的方式(例如模式更改),通常需要明显的(甚至是巨大的)工程量来实现。
单节点主从系统也提供了事务支持(即使不是强一致性)。然而,跨分片协调交易的难度是如此的琐碎和复杂,许多分片系统是决定彻底放弃它们的。
什么是主主可用性?
主主可用性意味着数据库至少有两个主节点,它们对数据进行分片并执行对数据库的写入。主主可用性代表了从主从的演变,通过让集群中的节点提供读写服务,使数据库能够扩展到单台机器之外。
考虑到分片数据库难以管理且功能不全,工程师们开始开发至少可以解决其中一个问题的系统。这时候出现的是仍然不支持事务的系统,但管理起来已经非常容易。随着对应用程序正常运行时间的需求不断增加,帮助团队满足其 SLA 的决定是很明智的。
这些系统背后的动机是每个实例节点都可以包含集群的部分(或全部)数据,并为其提供读取和写入服务。每当一个节点收到写入请求时,它都会将更改传播到所有其他需要它的副本的节点。为了处理两个节点对同一个键值写入的情况,任何一个节点的转换在提交之前都会被送入冲突解决算法。鉴于每个站点都是“活跃的”,因此被称为主主。
因为每台服务器都可以对其所有数据进行读写,所以分片更容易在算法上实现,并使部署更易于管理。
在可用性方面,主主非常出色。如果一个节点发生故障,客户端只需重定向到另一个确实包含数据的节点。只要数据的单个副本处于活动状态,就可以为其提供读取和写入服务。
虽然这种方案在高可用性方面非常出色,但其设计在一致性和数据正确性方面存在根本性的问题。因为每个实例节点都可以处理键值的写入(在故障转移场景中也是如此),所以它在处理数据时保持数据完全同步是非常困难的。该方案通常是通过冲突解决算法来调解实例之间的冲突,而该算法对如何“消除”不一致性的决策是粗粒度的。
由于该解决方案是事后完成的,是在客户端已经收到有关程序的响应之后——并且理论上已经根据该响应执行了其他业务逻辑——主主复制很容易在数据中生成异常。
然而,考虑到正常运行时间的溢价,停机成本被认为大于潜在异常的成本,因此主主成为主要的复制类型。
大规模正确性
共识和多活可用性
主主似乎解决了基础设施面临的主要问题——提供高可用性。但它只是通过放弃事务来做到这一点,这使得系统在强一致性需求的满足上并不那么可信。
例如,谷歌在其广告业务中使用了一个庞大而复杂的 MySQL 分片系统,该系统严重依赖 SQL 的表达能力来任意查询数据库。因为这些查询通常依赖二级索引来提高性能,所以它们必须与它们所派生的数据保持完全一致。
最终,这个系统变得足够大,开始导致分片 MySQL 出现问题,工程师开始设想如何解决这样的问题:既要有大规模可伸缩的系统,又要提供业务所需的强一致性。主主缺乏事务支持意味着它不应该是一个可选项,因此他们不得不设计一些新东西。最终,他们用这样的一个系统解决了问题,这是一个基于共识复制的系统,既能保证一致性,又能提供高可用性。
使用共识复制,写入被提议到一个节点,然后被复制到一些其他节点。一旦大多数节点确认写入,就可以提交。
共识和高可用性
这里的关键概念是,共识复制是介于同步和异步复制之间的一种机制:您可以指定任意数量的节点来进行同步,但这些节点是哪些并不重要。这意味着集群可以容忍少数节点宕机,而不会影响系统的可用性。(处理被关机服务器流量等的注意事项)
然而,共识的代价是它需要节点与其他节点进行通信以执行写入。虽然您可以采取一些措施来减少节点之间产生的延迟,例如将它们放在同一个可用区中,但这需要和高可用性一起权衡考虑。
例如,如果所有节点都在同一个数据中心,它们之间的通信速度很快,但如果整个数据中心离线,你的服务也不会独活。将您的节点分散到多个数据中心可能会增加写入所需的延迟,但却可以提高你的可用性,就算整个数据中心都离线了,你的应用也仍然在线。
什么是多主可用性?
多主可用性要求数据库至少具有三个活动节点,每个活动节点都可以对集群中的任何数据进行读写而不产生冲突。
CockroachDB 实现了Google Spanner 论文中的大部分内容(但值得注意的是,它不需要原子钟),包括那些超越共识复制之外的特性,这些特性使可用性变得更简单。为了描述其工作原理并将其与主主区分开来,我们创造了术语多主可用性。
主主 vs. 多主
主主通过允许集群中的任何节点为其键值提供读写服务来实现可用性,但只有在提交写之后才将其接受的更改传播给其他节点。
另一方面,多主可用性允许任何节点提供读写服务,但确保大多数副本在写入时保持同步,并且仅提供来自最新版本副本的读取服务。
在高可用性方面,主主只需要一个副本即可同时用于读写,而多主则需要大多数副本在线才能达成共识(这仍然允许系统内部出现部分故障)。
显然这些数据库在可用性方面的不同表现源于系统在对一致性方面处理的差异。主主数据库在大多数情况下都会努力工作写入数据,但是不能保证客户端现在或将来能够读取到该数据。而多主数据库仅在可以保证以后可以以一致的方式读取数据时才接受写入。
回顾与展望
在过去的 30 年中,数据库复制和可用性取得了长足的进步,现在已经支持全球范围内部署,感觉就像它们永远不会不受欢迎。该领域的首次尝试通过主从复制奠定了重要的基础,但最终,我们需要更好的可用性和更大的规模。
在这个领域,业界发展出了两种主要的数据库类型:其中主从用于满足那些主要关注快速写入的应用程序,而多主则服务于那些对一致性有需要的应用程序。
我们都期待有一天,我们可以利用量子纠缠并转向下一代数据库类型:可管理的分布式数据库。
译者介绍
崔莹峰,51CTO社区编辑,一名70后程序员,拥有10多年工作经验,长期从事 Java 开发,架构设计,容器化等相关工作。精通Java,熟练使用Maven、Jenkins等Devops相关工具链,擅长容器化方案规划、设计和落地。