原创分享 分布式从 ACID、CAP、BASE 的理论推进

aceld · 2020年03月27日 · 915 次阅读
本帖已被设为精华帖!

原创声明 作者: 刘丹冰 Aceld,微信公众号同名

作为当今互联网后端技术栈工程师、无论 Golang、Java 或者其他系,分布式的理论概念都逐步成为必备理论基础知识之一, 本文主要讨论分布式的 CAP 理论的推进,这是你走进分布式大门的第一块敲门砖。

提纲: 一、从本地事务到分布式理论 二、ACID 理论 三、CAP 理论 四、CAP 理论 “3 选 2” 论证 五、BASE 理论


附加:分布式概念 分布式实际上就是单一的本地一体解决方案,在硬件或者资源上不够业务需求,而采取的一种分散式多节点,可以扩容资源的一种解决思路。它研究如何把一个需要非常巨大的计算能力才能解决的问题分成许多小的部分,然后把这些部分分配给多个计算机进行处理,最后把这些计算结果综合起来得到最终的结果。


那么在了解分布式之前,我们应该从一体式的构造开始说明。

一、从本地事务到分布式理论

理解第一个问题就是"事务"

事务提供一种机制将一个活动涉及的所有操作纳入到一个不可分割的执行单元,组成事务的所有操作只有在所有操作均能正常执行的情况下方能提交,只要其中任一操作执行失败,都将导致整个事务的回滚。

简单地说,事务提供一种 “ 要么什么都不做,要么做全套(All or Nothing)” 机制。

二、ACID 理论

​ 事务是基于数据进行操作,需要保证事务的数据通常存储在数据库中,所以介绍到事务,就不得不介绍数据库事务的ACID特性,指数据库事务正确执行的四个基本特性的缩写。包含:

  • 原子性(Atomicity)

  • 一致性(Consistency)

  • 隔离性(Isolation)

  • 持久性(Durability)

(1) 原子性(Atomicity)

​ 整个事务中的所有操作,要么全部完成,要么全部不完成,不可能停滞在中间某个环节。

例如:银行转账,从 A 账户转 100 元至 B 账户:

A、从 A 账户取 100 元

B、存入 100 元至 B 账户。 这两步要么一起完成,要么一起不完成,如果只完成第一步,第二步失败,钱会莫名其妙少了 100 元。

(2) 一致性(Consistency)

在事务开始之前和事务结束以后,数据库数据的一致性约束没有被破坏。

例如:现有完整性约束 A+B=100,如果一个事务改变了 A,那么必须得改变 B,使得事务结束后依然满足 A+B=100,否则事务失败。

(3) 隔离性(Isolation)

​ 数据库允许多个并发事务同时对数据进行读写和修改的能力,如果一个事务要访问的数据正在被另外一个事务修改,只要另外一个事务未提交,它所访问的数据就不受未提交事务的影响。隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致。

例如:现有有个交易是从 A 账户转 100 元至 B 账户,在这个交易事务还未完成的情况下,如果此时 B 查询自己的账户,是看不到新增加的 100 元的。

(4) 持久性(Durability)

​ 事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失。

​ 本地事务 ACID 实际上可用” 统一提交,失败回滚 “几个字总结,严格保证了同一事务内数据的一致性!

而分布式事务不能实现这种ACID。因为有 CAP 理论约束。接下来我们来了解一下,分布式中是如何保证以上特性的,那么就有了一个著名的 CAP 理论。


三、CAP 理论

​ 在设计一个大规模可扩放的网络服务时候会遇到三个特性:一致性(consistency)、可用性(Availability)、分区容错(partition-tolerance)都需要的情景.

​ CAP 定律说的是在一个分布式计算机系统中,一致性,可用性和分区容错性这三种保证无法同时得到满足,最多满足两个。

如上图,CAP 的三种特性只能同时满足两个。而且在不同的两两组合,也有一些成熟的分布式产品。

接下来,我们来介绍一下 CAP 的三种特性,我们采用一个应用场景来分析 CAP 中的每个特点的含义。

该场景整体分为 5 个流程:

流程一、客户端发送请求 (如:添加订单、修改订单、删除订单)

流程二、Web 业务层处理业务,并修改存储成数据信息

流程三、存储层内部 Master 与 Backup 的数据同步

流程四、Web 业务层从存储层取出数据

流程五、Web 业务层返回数据给客户端

(1) 一致性 Consistency

all nodes see the same data at the same time

一旦数据更新完成并成功返回客户端后,那么分布式系统中所有节点在同一时间的数据完全一致。

在 CAP 的一致性中还包括强一致性、弱一致性、最终一致性等级别,稍后我们在后续章节介绍。

一致性是指写操作后的读操作可以读取到最新的数据状态,当数据分布在多个节点上,从任意结点读取到的数据都是最新的状态。

一致性实现目标:

  • Web 业务层向主 Master 写数据库成功,从 Backup 读数据也成功。

  • Web 业务层向主 Master 读数据库失败,从 Backup 读数据也失败。

必要实现流程:

写入主数据库后,在向从数据库同步期间要将从数据库锁定,待同步完成后再释放锁,以免在新数据写入成功后,向从数据库查询到旧的数据。

分布式一致性特点:

  1. 由于存在数据同步的过程,写操作的响应会有一定的延迟。
  2. 为了保证数据一致性会对资源暂时锁定,待数据同步完成释放锁定资源。
  3. 如果请求数据同步失败的结点则会返回错误信息,一定不会返回旧数据。

(2) 可用性 (Availability)

Reads and writes always succeed

服务一直可用,而且是正常响应时间。

对于可用性的衡量标准如下:

可用性分类 可用水平(%) 一年中可容忍停机时间
容错可用性 99.9999 <1 min
极高可用性 99.999 <5 min
具有故障自动恢复能力的可用性 99.99 <53 min
高可用性 99.9 <8.8h
商品可用性 99 <43.8 min

可用性实现目标:

  • 当 Master 正在被更新,Backup 数据库接收到数据查询的请求则立即能够响应数据查询结果。

  • backup 数据库不允许出现响应超时或响应错误。

必要实现流程:

  1. 写入 Master 主数据库后要将数据同步到从数据库。

  2. 由于要保证 Backup 从数据库的可用性,不可将 Backup 从数据库中的资源进行锁定。

  3. 即时数据还没有同步过来,从数据库也要返回要查询的数据,哪怕是旧数据/或者默认数据,但不能返回错误或响应超时。

分布式可用性特点:

所有请求都有响应,且不会出现响应超时或响应错误。

(3) 分区容错性 (Partition tolerance)

the system continues to operate despite arbitrary message loss or failure of part of the system

分布式系统中,尽管部分节点出现任何消息丢失或者故障,系统应继续运行。

通常分布式系统的各各结点部署在不同的子网,这就是网络分区,不可避免的会出现由于网络问题而导致结点之间通信失败,此时仍可对外提供服务。

分区容错性实现目标:

  • 主数据库向从数据库同步数据失败不影响读写操作。

  • 其一个结点挂掉不影响另一个结点对外提供服务。

必要实现流程:

  1. 尽量使用异步取代同步操作,例如使用异步方式将数据从主数据库同步到从数据,这样结点之间能有效的实现松耦合。
  2. 添加 Backup 从数据库结点,其中一个 Backup 从结点挂掉其它 Backup 从结点提供服务。

分区容错性特点:

分区容忍性分是布式系统具备的基本能力。

四、CAP 的” 3 选 2“证明

(1) 基本场景

在小结中,我们主要介绍 CAP 的理论为什么不能够 3 个特性同时满足。

如上图,是我们证明 CAP 的基本场景,分布式网络中有两个节点 Host1 和 Host2,他们之间网络可以连通,Host1 中运行 Process1 程序和对应的数据库 Data,Host2 中运行 Process2 程序和对应数据库 Data。

(2) CAP 特性

如果满足一致性(C):那么Data(0) = Data(0).

如果满足可用性(A): 用户不管请求 Host1 或 Host2,都会立刻响应结果。

如果满足分区容错性(P): Host1 或 Host2 有一方脱离系统 (故障), 都不会影响 Host1 和 Host2 彼此之间正常运作。

(3) 分布式系统正常运行流程

如上图,是分布式系统正常运转的流程。

A、用户向Host1主机请求数据更新,程序Process1更新数据库Data(0)Data(1)

B、分布式系统将数据进行同步操作,将Host1中的Data(1)同步的Host2中`Data(0),使Host2中的数据也变为Data(1)

C、当用户请求主机Host2时,则Process2则响应最新的Data(1)数据

根据 CAP 的特性:

  • Host1Host2的数据库Data之间的数据是否一样为一致性 (C)

  • 用户对Host1Host2的请求响应为可用性 (A)

  • Host1Host2之间的各自网络环境为分区容错性 (P)

当前是一个正常运作的流程,目前 CAP 三个特性可以同时满足,也是一个理想状态,但是实际应用场景中,发生错误在所难免,那么如果发生错误 CAP 是否能同时满足,或者该如何取舍?


(4) 分布式系统异常运行流程

假设Host1Host2之间的网络断开了,我们要支持这种网络异常,相当于要满足分区容错性(P),能不能同时满足一致性(C)可用响应性(A)呢?

假设在 N1 和 N2 之间网络断开的时候,

A、用户向Host1发送数据更新请求,那Host1中的数据Data(0)将被更新为Data(1)

B、弱此时Host1Host2网络是断开的,所以分布式系统同步操作将失败,Host2中的数据依旧是Data(0)

C、有用户向Host2发送数据读取请求,由于数据还没有进行同步,Process2没办法立即给用户返回最新的数据 V1,那么将面临两个选择。

第一,牺牲数据一致性(c),响应旧的数据Data(0)给用户;

第二,牺牲可用性(A),阻塞等待,直到网络连接恢复,数据同步完成之后,再给用户响应最新的数据Data(1)

这个过程,证明了要满足分区容错性(p)的分布式系统,只能在一致性(C)可用性(A)两者中,选择其中一个。

(5) "3 选 2"的必然性

通过 CAP 理论,我们知道无法同时满足一致性可用性分区容错性这三个特性,那要舍弃哪个呢?

CA 放弃 P:

一个分布式系统中,不可能存在不满足 P,放弃分区容错性(p),即不进行分区,不考虑由于网络不通或结点挂掉的问题,则可以实现一致性和可用性。那么系统将不是一个标准的分布式系统。我们最常用的关系型数据就满足了 CA,如下:

主数据库和从数据库中间不再进行数据同步,数据库可以响应每次的查询请求,通过事务 (原子性操作) 隔离级别实现每个查询请求都可以返回最新的数据。

注意:

对于一个分布式系统来说。P 是一个基本要求,CAP 三者中,只能在 CA 两者之间做权衡,并且要想尽办法提升 P。

CP 放弃 A

如果一个分布式系统不要求强的可用性,即容许系统停机或者长时间无响应的话,就可以在 CAP 三者中保障 CP 而舍弃 A。

放弃可用性,追求一致性和分区容错性,如 Redis、HBase 等,还有分布式系统中常用的 Zookeeper 也是在 CAP 三者之中选择优先保证 CP 的。

场景:

跨行转账,一次转账请求要等待双方银行系统都完成整个事务才算完成。

AP 放弃 C

放弃一致性,追求分区容忍性和可用性。这是很多分布式系统设计时的选择。实现 AP,前提是只要用户可以接受所查询的到数据在一定时间内不是最新的即可。

通常实现 AP 都会保证最终一致性,后面讲的 BASE 理论就是根据 AP 来扩展的。

场景 1

淘宝订单退款。今日退款成功,明日账户到账,只要用户可以接受在一定时间内到账即可。

场景 2:

12306 的买票。都是在可用性和一致性之间舍弃了一致性而选择可用性。

在 12306 买票的时候提示有票(但是可能实际已经没票了),用户正常去输入验证码,下单。但是过了一会系统提示下单失败,余票不足。这其实就是先在可用性方面保证系统可以正常的服务,然后在数据的一致性方面做了些牺牲,会影响一些用户体验,但是也不至于造成用户流程的严重阻塞。

但是,我们说很多网站牺牲了一致性,选择了可用性,这其实也不准确的。就比如上面的买票的例子,其实舍弃的只是强一致性。退而求其次保证了最终一致性。也就是说,虽然下单的瞬间,关于车票的库存可能存在数据不一致的情况,但是过了一段时间,还是要保证最终一致性的。

(6) 总结:

CA 放弃 P:如果不要求 P(不允许分区),则 C(强一致性)和 A(可用性)是可以保证的。这样分区将永远不会存在,因此 CA 的系统更多的是允许分区后各子系统依然保持 CA。

CP 放弃 A:如果不要求 A(可用),相当于每个请求都需要在 Server 之间强一致,而 P(分区)会导致同步时间无限延长,如此 CP 也是可以保证的。很多传统的数据库分布式事务都属于这种模式。

AP 放弃 C:要高可用并允许分区,则需放弃一致性。一旦分区发生,节点之间可能会失去联系,为了高可用,每个节点只能用本地数据提供服务,而这样会导致全局数据的不一致性。现在众多的 NoSQL 都属于此类。

五、思考

思考:按照 CAP 理论如何设计一个电商系统?

  • 首先个电商网站核心模块有用户,订单,商品,支付,促销管理

1、对于用户模块,包括登录,个人设置,个人订单,购物车,收藏夹等,这些模块保证 AP,数据短时间不一致不影响使用。 2、订单模块的下单付款扣减库存操作是整个系统的核心,CA 都需要保证,极端情况下面牺牲 A 保证 C 3、商品模块的商品上下架和库存管理保证 CP 4、搜索功能因为本身就不是实时性非常高的模块,所以保证 AP 就可以了。 5、促销是短时间的数据不一致,结果就是优惠信息看不到,但是已有的优惠要保证可用,而且优惠可以提前预计算,所以可以保证 AP。 6、支付这一块是独立的系统,或者使用第三方的支付宝,微信。其实 CAP 是由第三方来保证的,支付系统是一个对 CAP 要求极高的系统,C 是必须要保证的,AP 中 A 相对更重要,不能因为分区,导致所有人都不能支付

六、分布式 BASE 理论

​ CAP 不可能同时满足,而分区容错性(P)是对于分布式系统而言是必须的。如果系统能够同时实现 CAP 是再好不过的了,所以出现了 BASE 理论。

(1) BASE 理论

通用定义

BASE 是Basically Available(基本可用)Soft state(软状态)Eventually consistent(最终一致性)三个短语的简写。

BASE 是对 CAP 中一致性和可用性权衡的结果,其来源于对大规模互联网系统分布式实践的总结,是基于 CAP 定理逐步演化而来的,其核心思想是即使无法做到强一致性,但每个应用都可以根据自身的业务特点,采用适当的方法来使系统达到最终一致性

两个对冲理念:ACID 和 BASE

ACID是传统数据库常用的设计理念,追求强一致性模型。

BASE支持的是大型分布式系统,提出通过牺牲强一致性获得高可用性

(2) Basically Available(基本可用)

实际上就是两个妥协。

  • 对响应上时间的妥协:正常情况下,一个在线搜索引擎需要在 0.5 秒之内返回给用户相应的查询结果,但由于出现故障(比如系统部分机房发生断电或断网故障),查询结果的响应时间增加到了 1~2 秒。

  • 对功能损失的妥协:正常情况下,在一个电子商务网站(比如淘宝)上购物,消费者几乎能够顺利地完成每一笔订单。但在一些节日大促购物高峰的时候(比如双十一、双十二),由于消费者的购物行为激增,为了保护系统的稳定性(或者保证一致性),部分消费者可能会被引导到一个降级页面,如下:

(3) Soft state(软状态)

  • 原子性(硬状态) -> 要求多个节点的数据副本都是一致的,这是一种"硬状态"

  • 软状态(弱状态) -> 允许系统中的数据存在中间状态,并认为该状态不影响系统的整体可用性,即允许系统在多个不同节点的数据副本存在数据延迟。

(4) Eventually consistent(最终一致性)

上面说软状态,然后不可能一直是软状态,必须有个时间期限。在期限过后,应当保证所有副本保持数据一致性。从而达到数据的最终一致性。这个时间期限取决于网络延时,系统负载,数据复制方案设计等等因素。

稍微官方一点的说法就是:

系统能够保证在没有其他新的更新操作的情况下,数据最终一定能够达到一致的状态,因此所有客户端对系统的数据访问最终都能够获取到最新的值。

(5) BASE 总结

总的来说,BASE 理论面向的是大型高可用可扩展的分布式系统,和传统事务的 ACID 是相反的,它完全不同于 ACID 的强一致性模型,而是通过牺牲强一致性来获得可用性,并允许数据在一段时间是不一致的。

参考:

https://blog.csdn.net/weixin_44062339/article/details/99710968

https://blog.csdn.net/w372426096/article/details/80437198

https://www.solves.com.cn/it/cxkf/bk/2019-09-24/5229.html

https://www.jianshu.com/p/46b90dfc7c90

https://www.jianshu.com/p/9cb2a6fa4e0e

https://www.jianshu.com/p/68c7c16b3fbd


### 关于作者:

mail: danbing.at@gmail.com github: https://github.com/aceld 原创书籍 gitbook: http://legacy.gitbook.com/@aceld

创作不易, 共同学习进步, 欢迎关注作者, 回复"zinx"有好礼

作者微信公众号


文章推荐

开源软件作品

(原创开源) Zinx-基于 Golang 轻量级服务器并发框架-完整版 (附教程视频)

(原创开源) Lars-基于 C++ 负载均衡远程调度系统-完整版

精选文章

典藏版-Golang 调度器 GMP 原理与调度全分析

Golang 三色标记、混合写屏障 GC 模式图文全分析

最常用的调试 golang 的 bug 以及性能问题的实践方法?

Golang 中的 Defer 必掌握的 7 知识点

Golang 中的局部变量 “何时栈?何时堆?”

使用 Golang 的 interface 接口设计原则

流?I/O 操作?阻塞?epoll?

深入浅出 Golang 的协程池设计

Go 语言构建微服务一站式解决方案


更多原创文章干货分享,请关注公众号
  • 加微信实战群请加微信(注明:实战群):gocnio
astaxie 将本帖设为了精华贴 03月27日 15:11
cloudy GoCN 每日新闻 (2020-03-27) 中提及了此贴 03月27日 18:35
astaxie GoCN 每日新闻 (2020-03-29) 中提及了此贴 03月29日 13:38
需要 登录 后方可回复, 如果你还没有账号请点击这里 注册