一、学短语
BASE 全称是由三个短语缩写组成(不是四个)BA + S + E:
- Basically Available :基本可用
- Soft State :软状态
- Eventually Consistent :最终一致性
二、了解背景
在分布式系统中,受单机资源能力上限的约束,当数据规模扩大时不得不选择水平扩展,而任何水平扩展策略都是基于数据分区的。当我们不得不分区,而分区之后又无法避免网络异常的状况下,就不得不接受分区容错性(P)。回忆上一节《CAP 定理(CAP theorem)》 中介绍的分布式系统的痛点,对于共享数据系统,数据一致性(C)、系统可用性(A) 和 分区容容错性(P) 只能同时保证两个。
在三者只能选其二的无奈之下,分区容错性(P)又必须接受,剩下的就只能二选一,要么弱化一致性来保证系统的可用性,要么保证一致性而接受异常时系统的不可用。
三、BASE 理论的提出
eBay 的架构师 Dan Pritchett 08年发表论文《Base: An Acid Alternative》,论文前半部分以 CAP 定理为理论基础,来探讨数据库 ACID 特性的缺陷,核心逻辑是这样:
- 首先面对数据量增大的情况对数据库进行分区是必要的
- 当数据库分区后,基于 CAP 定理则只能在可用性和一致性之间选择其中一个
- 数据库系统的 ACID 特性要求的是强制一致性,这就导致有些情况下的系统的可用性很难保障
- 但忽视可用性又是不行的
为解决这种困境,他提出了 BASE 理论,强调要接受数据库非稳定一致性的状态(可能存在中间状态),这样才能极大的提升数据库的可扩展性;同时也说明 BASE 中的可用性是通过支持部分故障而不是整个系统故障来实现的。相信至此你对基本可用以及软状态的本意就有那么点感觉了。
对于一致性,论文的后半部分引用现实中的一些案例来指出,要因地制宜地采取策略达成一致性,如引入缓存提升性能会导致有限时间的状态不一致,引入持久化消息队列总能保障最终一致性。
四、理论认知的演进
从 2008 年 BASE 理论被提出后,后人不断对其讨论解读,不再仅限于数据库的 ACID 范畴( 关于 ACID 原则可查看前文《ACID原则-大道至简》),而且逐渐演进为用以下话术来描述:
4.1 基本可用(Basically Available)
当系统遇到某些不可抗力的异常时,仍然能够保障“可用性”,会在限定时间内返回一个明确的结果,主要体现在以下 2 方面:
- 时效性的变化:响应时间可以适当延长
- 功能完整性的变化:功能降级,但被降级的功能要尽量少,且即使降级返回的结果也必须是明确的,不能让用户困惑
4.2 软状态(Soft State)
允许系统中的数据存在中间状态,并认为该中间状态的存在不会影响系统的整体可用性,允许系统内节点之间的数据同步存在延时,客户端会读到各副本数据达到一致前的中间状态。
4.3 最终一致性(Eventually Consistent)
BASE 论文中对于最终一致性的解释简明清晰,稍稍遗憾的是读完之后的感觉是味道鲜美但没有饱腹感。无独有偶,在同年 12 月 AWS 的 CTO Werner Vogels 也写了一篇很经典的 《Eventually Consistent - Revisited》,对最终一致性做了更详尽的描述,可谓鞭辟入里,让人顿开茅塞。当下主流的高质量的言论也多是复刻、摘录此文(咱也是😊)。
五、深入理解最终一致性
Werner Vogels 老前辈分别从客户端和服务端两个不同的视角来解读最终一致性:
- 客户端视角关注:如何观察数据更新
- 服务器端视角关注:更新如何在服务端的系统内流动,以及系统又能为更新提供哪些保证
5.1 设定示例环境
- 一个存储系统
一个提供了持久性和可用性的保证的大规模分布式的存储系统,从客户端的视角来看他是一个充满科技的黑匣子 - 客户端 A、B、C
这三个个客户端彼此是独立的,都能读写存储系统。
5.2 客户端视角的一致性
当客户端 A 对数据进行了更新,从客户端的视角来看一致性分为 3 种情况,差异就在于如何以及何时看到客户端 A 对存储系统中数据所做的更新:
- 强一致性
存储系统保证,三个客户端的任何后续访问都将返回更新后的值 - 弱一致性
存储系统不保证,后续访问会得到更新后的值。 需要符合一定条件才能获取更新后的值。 从客户端 A 更新操作结束,到所有客户端都能访问到更新,这中间存在一个不一致窗口期 - 最终一致性
是一种特殊的弱一致性形式;存储系统保证,若没有新的更新,最终所有访问都将返回最后一次更新的值。若没有发生故障,则可以根据通信延迟、系统负载以及同步方案中涉及的副本数等因素,综合考量确定不一致窗口的最大大小。
强一致很容易理解,而最终一致性受多种因素的影响,本质也是要适配各种场景的使用诉求,就衍生出一些重要的变体模型:
一致性变种 | 解释说明 |
因果一致性 | 如果客户端 A 通知客户端 B 它更新了一项数据,后续客户端 B 的访问会返回更新后的值,并且保证写入将取代之前的写入。与客户端 A 没有因果关系的客户端 C 的访问是遵循一般的最终一致性规则 |
读己之所写(Read-Your-Writes) 一致性 | 这是一种重要的模型,客户端 A 更新一个数据项之后总会得到更新后的值,永远不会得到旧值。这是因果一致性的一种特例。 |
会话(Session)—致性 | 这是上个模型的实践版本,其中是一个客户端在一个会话上下文中访问存储系统。 只要这个会话存在,系统保证读你所写一致性。 如果会话因为一些特定场景失效,则一个新的会话需要被创建,并且一致性保证不会跨会话 |
单调(Monotonic)读一致性 | 如果一个客户端读取到了一个更新后的值,任何后续访问都不会得到之前的值 |
单调写一致性 | 系统保证来自同一个客户端的写操作顺序执行 |
以上一致性变种模型中的部分模型还可以组合使用,需因地制宜,这样看起来还是有点意思哈,有没有?
5.2 服务端视角的一致性
从服务端来看,如何尽快地将更新后的数据同步到整个系统,减小达到最终一致性的时间窗口,则可提高系统的可用性并提升用户体验。
探讨这个问题,要看分布式数据系统以下几个要素的数值:
- N : 存储数据副本的节点的数量
- W : 更新成功所需确认已成功完成更新的副本数量
- R : 一次数据对象读取要访问的副本的数量
当这几个要素的值以不同的方式组合后,除了一致性的效果不同,整体呈现的三维(可用性、性能、一致性)也不同:
组合逻辑 | 效果 | 示例 |
W+R>N | 强一致性 | 写的节点和读的节点重叠,例如,典型的一主一备同步复制的关系型数据库(N=2, W=2, R=1),不管读的是主库还是备库的数据,都是一致的 |
W+R≤N | 弱一致性 | 对于一主一备异步复制的关系型数据库(N=2,W=1,R=1),如果读的是备库,则可能无法读取主库已经更新过的数据,表现为弱一致性 |
设置不同的 N、W、R 值,在可用性、性能、一致性之间取舍,以适配不同的场景诉求,如:
- N≥3
一般为了保证高可用而如此设置 - N=3(W=2 R=2)
仅关注容错的系统通常使用配置 - N很大且 R = 1
提供非常高的读取性能:复制超出容错所需的数据副本,单节点读取就返回结果 - N=W 且 R=1
提升读性能,但要写入多个节点,写性能变低;任何一个写节点失效,都会导致写失败,因此可用性会降低 - N=R 且 W=1
提升写性能,只需要一个节点写入成功即可,读全部节点能尽量保证读到新数据,但读性能不高;写故障的情况下也无法保证持久性,也无法保障读到新数据。
六、总结
总的来说,BASE 理论主要面向的是大型、高可用、可扩展的分布式存储系统,它完全不同于 ACID 的强一致性模型,而是强调通过牺牲强一致性来换取可用性。
分布式存储系统通常通过主备(primary-backup)提升可靠性,主备之间采用同步或异步模式实现各副本数据一致;同步模式数据一致,但性能较差;异步模式通常通过 log 传输的方式,会有延迟,而其不一致性窗口就取决于 log 传送的周期;在异步模式下如果主服务在 log 送达之前宕机,主从切换后读取的将会是旧值。
刚好遇到过一个MySQL同步机制的 BUG,MySQL 主库处理更新事务的 commit 太慢,canal 监听到主库更新的binlog之后,后续逻辑再查询主库居然是旧值 ,这个问题的发现和定位还是费了一番功夫的,已整理成有趣的剧本方便阅读了解:
大规模可靠的分布式系统中的数据不一致是客观的,是否可以接受取决于客户端应用程序。开发人员需要意识到一致性保证是由存储系统提供的,并且在设计、开发应用程序时需要将其不一致的可能性考虑在内。
最后说一句(请关注,莫错过)
如果这篇文章对您有帮助,或者有所启发的话,欢迎关注公众号【 架构染色 】进行交流和学习。您的支持是我坚持写作最大的动力。
分布式事务-理论先行合集
- CAP定理(CAP theorem)
- ACID原则-大道至简
- 溯本清源解吃透BASE理论
- 二阶段提交(进行中)
- 三阶段提交(进行中)
- 二阶段提交变种-TCC模型(进行中)
- 二阶段提交变种-AT模型(进行中)