0. 背景
本文记录一些高可用的内容,和数据库在高可用方面的演进过程。
1. 概念
可用性: 即软件系统在一段时间内提供 有用资源 的能力。
高可用性 描述了一个周期内的功能连续可用的绝对程度,可表示为正常运行时间和停机时间之间的关系,如下公式:
A = 100 – (100*D/U)
备注:A 表示可用性;D 表示 非计划停机时间;U 表示正常运行时间。
正常运行时间和可用性并不是同一个概念。一个系统可能会运行一个完整的测量周期,但可能由于网络中断或相关支持系统的停机而不可用。停机和不可用是同义词。
一个系统可能会持续运行很长的一段时间,可能由于网络中断或因关联的系统停机而导致该系统不可用。
正常的计划停机 和 不可用 是不同的内容。
测量可用性
将可用性描述为“9”个数,如表1所示。
可用性 % | 停机时间/分钟 | 停机时间/年 | 供应商术语 |
---|---|---|---|
90 | 52,560.00 | 36.5 天 | 一个9 |
99 | 5,256.00 | 4 天 | 两个9 |
99.9 | 525.60 | 8.8 小时 | 三个9 |
99.99 | 52.56 | 53 分钟 | 四个9 |
99.999 | 5.26 | 5.3 分钟 | 五个9 |
99.9999 | 0.53 | 32 秒 | 六个9 |
表格展示了: 可用性占每年总正常运行时间的百分比
2. 如何设计来做到高可用
保证系统高可用,架构设计的核心准则是:冗余 和 故障转移。
单点系统的问题是,挂了就完全不可用了,服务会受影响。如果有冗余备份,其他后备的系统能够顶上,保证服务继续可用。
有了冗余之后,遇到故障需要人工介入恢复是来不及的。所以,又往往是通过“自动故障转移”来使得快速切换到备份系统来实现高可用。
常见的互联网分布式架构是:
前端 ---> 反向代理 --> WEB应用 --> 服务 --> 数据库(及缓存)
其中,高可用可涉及到上面每个节点的高可用保障,我们看下数据的高可用架构的演变过程。
3. 数据库的高可用简史
3.1 主从备份(Active-Passive)
最开始,数据库运行在单台机器上,只有一个节点负责处理所有的读取和写入操作。数据库要么在运行,要么被关闭。
这时,计算机被持续访问,停机时间会直接影响到用户。由于计算机不停地运行,也更有可能发生故障。最明显的解决方案是拥有多台可以处理请求的计算机。
主备(Active-Passive)复制机制:
一个解决方案是让单个节点处理读写操作,同时将它的 状态变更 同步到辅助节点上。
主备机制通过备份主节点来提高可用性:当主节点发生故障,将流量重定向到备用节点,并将其提升为新的主节点。
但是,如果备用节点发生故障该怎么办?如果备用节点不可用,当整个系统挂掉时,这种机制就变得毫无意义(但在进行同步复制时,这种情况真的有可能发生)。
为了进一步提高可用性,可以异步复制数据
虽然架构看起来是一样的,但在主节点或备用节点发生故障时,都不会影响数据库的可用性。
异步主备机制仍然存在重大不足:
- 当主节点发生故障时,任何尚未复制到备用节点的数据都可能丢失。
- 因为依靠单台机器处理流量,所以仍然受限于单台机器的最大可用资源。
3.2 分片
从单机到分布式:扩展到多台机器
随着互联网的快速发展,业务需求在规模和复杂性方面也有所增长。于是产生了“始终在线”的高可用性要求,就有了在多台计算机之间分布数据库。
将数据库分布在多台计算机上,通过分片将主备复制变成了更具可扩展性的方式。
分片
将集群的数据按照某个值(例如行数或主键中的唯一值)进行拆分,并将这些数据段分布在多个节点上,每个节点都有一个主备对。然后在集群前添加某种路由技术,将客户端定向到正确的节点上。
通过分片可以在多台计算机之间分配工作负载,提高吞吐量,并通过容忍更多的部分故障来获得更大的弹性。
缺点:
- 系统分片极其复杂,会给团队带来巨大的运营负担。
- 管理分片的任务可能会变得非常繁重,以至于路由逻辑最终会逐步渗透到应用程序的业务逻辑中。
- 如果你想要修改系统的分片方式(例如模式变更),通常会导致巨大的工作量。
而跨分片协调事务非常困难和复杂,以至于很多分片系统完全放弃了事务支持。
3.3 双活(Active-Active)
分片数据库难以管理,工程师们开发出了新的方案,这个方案仍然不支持事务,但是它更容易管理。这个方案的特点:
- 每个节点可以包含集群的全部或部分数据,并负责处理读写操作
- 当一个节点接收到写入操作时,将变更传播到需要数据副本的所有节点。
- 为了处理两个节点接收到相同键的情况,在提交之前,需要通过特殊算法来解决冲突。
因为每个节点都是“活跃”的,因此这种模式被称为双活(Active-Active)
由于每个节点都可以处理所有数据的读写操作,因此更容易通过算法进行分片,也易于管理部署。
如果一个节点发生故障,只需将客户端重定向到另一个节点上。只要数据的有一个副本处于活动状态,就可以处理读写操作。
缺点:
- 一致性方面存在不足。
- 双活复制很容易导致数据出现异常。
因为每个节点都可以处理键的写入,所以在处理数据时要保持数据完全同步变得非常困难。它通过冲突算法来调解节点之间的冲突,也仅会做出一些粗粒度的决策。
冲突处理方案是在事后完成的,也就是在客户端已经收到响应之后,就可能导致,客户端在收到响应后还执行了其他业务逻辑。所以双活复制很容易导致数据出现异常。
不过,总要做出取舍。考虑到要高可用,停机比潜在的数据异常成本更高,因此双活成了主要的复制类型。
3.4 多活:基于共识的大规模一致性
双活解决了一些可用性问题和便于管理,但仍然忽略了事务,而需要强一致性的系统仍然找不到合适的解决方案。工程师设计了新的方案,它是一个基于共识复制的系统,它可以保证一致性,也可以提供高可用性。
特点:
- 在使用共识复制时,写入操作被传给一个节点,然后再复制到其他节点。
- 如果大多数节点已经确认写入,就可以提交写入操作。
共识复制处于同步和异步复制之间的一个最佳位置。它需要其中的部分节点保持同步就够了,至于是哪些节点就无关紧要。
这意味着集群可以容忍少数节点宕机,而不会影响系统的可用性
缺点:延迟。
要做到共识的代价是:它需要多个节点间进行通信以执行写入操作。虽然可以采取一些措施来减少节点之间的延迟,例如将它们放在同一可用区域中,但这需要在可用性方面做出权衡。例如:
- 如果所有节点都在同一个数据中心,节点之间的通信速度快了,但是如果整个数据中心发生故障,那么可用性就不复存在。
- 如果将节点扩展到多个数据中心,某个数据中心出了问题时保证了可用性,但是可能会增加写入所需的延迟。
3.5 双活与多活的对比
项 | 双活 | 多活 |
---|---|---|
响应方式 | 任意一个写入成功,即响应成功 | 大多数副本完成,才响应成功 |
- | 允许任意节点提供读写 | 允许任意节点提供读写,并且只让最新的副本处理读取操作 |
一致性 | 任意一个节点完成了“提交写入”后,才将数据变更传播到其他节点 | 确保大多数副本都写入后才提交,保持了同步。才响应“完成了提交” |
- | 单个存活就行 | 需要大多数副本节点存活才行 |
读取数据时 | 单个写入成功,其他副本可能失败,因此不能保证一定读取到写入的数据 | 只在能够保证一致的方式读取到数据的情况下才接受写入。 |
总结 | 关注快速读写 | 关注一致性 |
4. 展望
主备复制为这个领域奠定了重要的基础,但最终,我们需要更好的可用性和更大的规模。业界开发了两种主要的数据库范式:双活主要用于关注快速写入的应用程序,而多活主要用于关注一致性的应用程序。
我们也期待着下一个范式。
5. 参考
https://www.cockroachlabs.com/blog/brief-history-high-availability/
https://cloud.tencent.com/developer/article/1426456
https://zhuanlan.zhihu.com/p/43723276
https://www.cockroachlabs.com/blog/brief-history-high-availability