CAP详解及应用 您所在的位置:网站首页 cap原则指的是 CAP详解及应用

CAP详解及应用

2024-07-13 15:25| 来源: 网络整理| 查看: 265

什么是cap: 先套用百度百科上的说法。 CAP原则又称CAP定理,指的是在一个分布式系统中, Consistency(一致性)、 Availability(可用性)、Partition tolerance(分区容错性),三者不可得兼。 一致性(C):在分布式系统中的所有数据备份,在同一时刻是否同样的值。(等同于所有节点访问同一份最新的数据副本) 可用性(A):保证每个请求不管成功或者失败都有响应。 分区容忍性(P):系统中任意信息的丢失或失败不会影响系统的继续运作。

一致性和可用性还好理解。分区容忍性是什么呢,一个分布式系统里面,节点组成的网络本来应该是连通的。然而可能因为一些故障,使得有些节点之间不连通了,整个网络就分成了几块区域。数据就散布在了这些不连通的区域中。这就叫分区。当你一个数据项只在一个节点中保存,那么分区出现后,和这个节点不连通的部分就访问不到这个数据了。这时分区就是无法容忍的。提高分区容忍性的办法就是一个数据项复制到多个节点上,那么出现分区之后,这一数据项就可能分布到各个区里。容忍性就提高了。然而,要把数据复制到多个节点,就会带来一致性的问题,就是多个节点上面的数据可能是不一致的。要保证一致,每次写操作就都要等待全部节点写成功,而这等待又会带来可用性的问题。总的来说就是,数据存在的节点越多,分区容忍性越高,但要复制更新的数据就越多,一致性就越难保证。为了保证一致性,更新所有节点数据所需要的时间就越长,可用性就会降低。

拆开来讲如何增强一致性,可用性,分区容忍性: 如何增强一致性:数据同步更精确,花更多的时间保证同步,同步完成之前服务不可用。 如何增强可用性:数据同步的速度更快,或者是不等数据同步完成就直接可以访问。 如何增强分区容忍性:相同的数据放在更多的服务中即增加服务数量 如何实现cp,ap,ca呢: cp保证一致性和分区容忍性,结合上面的每个的实现方案,即花更多的时间保证同步,增加服务的数量。这就意味着服务将有更多的时间不可用即可用性降低

ap保证可用性和分区容忍性,增强可用性和分区容忍性即让数据同步速度更快,增加服务器数量。这显然不可能,只能是在同步未完成时就可访问,这样一致性必然降低。

ca增强一致性和可用性,就只能是加快同步速度咯,那怎样在保证一致性的同时又加快同步速度,那就只能是减少服务的数量咯。

cp,ap,ca应用: cp:注册中心 Zookeeper、Consul、Nacos,其中nacos可自由切换ap cp ;数据库:mongodb、hbase、redis等 ap:注册中心 Eureka、Nacos,其中nacos可自由切换ap cp;数据库:simpleDB、riak等 ca:数据库:mysql、postgres等

分布式锁 基于数据库实现分布式锁ca:利用表的 UNIQUE KEY idx_lock(method_lock)作为唯一主键,当进行上锁时进行 Insert 动作,数据库成功录入则以为上锁成功,当数据库报出 Duplicate entry 则表示无法获取该锁。不过这种方式对于单主却无法自动切换主从的 MySQL 来说,基本就无法实现 P 分区容错性(MySQL自动主从切换在目前并没有十分完美的解决方案)。 可以说这种方式强依赖于数据库的可用性,数据库写操作是一个单点,一旦数据库挂掉,就导致锁的不可用。 基于 Redis 实现分布式锁ap:Redis 单线程串行处理天然就是解决串行化问题,用来解决分布式锁是再适合不过。为了解决数据库锁的无主从切换的问题,可以选择 Redis 集群,或者是 Sentinel 哨兵模式,实现主从故障转移,当 Master 节点出现故障,哨兵会从 Slave 中选取节点,重新变成新的 Master 节点。哨兵模式故障转移是由 Sentinel 集群进行监控判断,当 Maser 出现异常即复制中止,重新推选新 Slave 成为 Master,Sentinel 在重新进行选举并不在意主从数据是否复制完毕具备一致性。所以 Redis 的复制模式是属于 AP 的模式。保证可用性,在主从复制中“主”有数据,但是可能“从”还没有数据。这个时候,一旦主挂掉或者网络抖动等各种原因,可能会切换到“从”节点,这个时候可能会导致两个业务线程同时获取得两把锁。 能不能使用 Redis 作为分布式锁?这个本身就不是 Redis 的问题,还是取决于业务场景。我们先要自己确认我们的场景是适合 AP 还是 CP , 如果在社交发帖等场景下,我们并没有非常强的事务一致性问题,Redis 提供给我们高性能的 AP 模型是非常适合的。但如果是交易类型,对数据一致性非常敏感的场景,我们可能要寻找一种更加适合的 CP 模型。 基于 Zookeeper 实现分布式锁:首先 ZK 的模式是 CP 模型,也就是说,当 ZK 锁提供给我们进行访问的时候,在 ZK 集群中能确保这把锁在 ZK 的每一个节点都存在。 ZK 的特性如下: 有序节点:当在一个父目录下如 /lock 下创建 有序节点,节点会按照严格的先后顺序创建出自节点 lock000001,lock000002,lock0000003,以此类推,有序节点能严格保证各个自节点按照排序命名生成。 临时节点:客户端建立了一个临时节点,在客户端的会话结束或会话超时,Zookepper 会自动删除该节点 ID。 事件监听:在读取数据时,我们可以对节点设置监听,当节点的数据发生变化(1 节点创建 2 节点删除 3 节点数据变成 4 自节点变成)时,Zookeeper 会通知客户端。 结合这几个特点,来看下 ZK 是怎么组合分布式锁: 业务线程 -1,业务线程 -2 分别向 ZK 的 /lock 目录下,申请创建有序的临时节点。 业务线程 -1 抢到 /lock0001 的文件,也就是在整个目录下最小序的节点,也就是线程 -1 获取到了锁。 业务线程 -2 只能抢到 /lock0002 的文件,并不是最小序的节点,线程 2 未能获取锁。 业务线程 -1 与 lock0001 建立了连接,并维持了心跳,维持的心跳也就是这把锁的租期。 当业务线程 -1 完成了业务,将释放掉与 ZK 的连接,也就是释放了这把锁。 究竟该用 CP 还是 AP 的分布式锁 首先得了解清楚我们使用分布式锁的场景,为何使用分布式锁,用它来帮我们解决什么问题,先聊场景后聊分布式锁的技术选型。无论是 Redis,ZK,例如 Redis 的 AP 模型会限制很多使用场景,但它却拥有了几者中最高的性能。Zookeeper 的分布式锁要比 Redis 可靠很多,但他繁琐的实现机制导致了它的性能不如 Redis,而且 ZK 会随着集群的扩大而性能更加下降。简单来说,先了解业务场景,后进行技术选型。 分布式事务,是怎么从 ACID 解脱,投身 CAP/BASE如果说到事务,ACID 是传统数据库常用的设计理念,追求强一致性模型,关系数据库的 ACID 模型拥有高一致性+可用性,所以很难进行分区。在微服务中 ACID 已经是无法支持,我们还是回到 CAP 去寻求解决方案,不过根据上面的讨论,CAP 定理中,要么只能 CP,要么只能 AP。如果我们追求数据的一致性而忽略可用性这个在微服务中肯定是行不通的,如果我们追求可用性而忽略一致性,那么在一些重要的数据(例如支付,金额)肯定出现漏洞百出,这个也是无法接受。 所以我们既要一致性,也要可用性。都要是无法实现的,但我们能不能在一致性上作出一些妥协,不追求强一致性,转而追求最终一致性,所以引入 BASE 理论。在分布式事务中,BASE 最重要是为 CAP 提出了最终一致性的解决方案,BASE 强调牺牲高一致性,从而获取可用性,数据允许在一段时间内不一致,只要保证最终一致性就可以。 分布式事务 在分布式系统中,要实现分布式事务,无外乎几种解决方案。方案各有不同,不过其实都是遵循 BASE 理论,是最终一致性模型: 两阶段提交(2PC) 补偿事务(TCC) 本地消息表 MQ 事务消息 两阶段提交(2PC) 阶段提交协议是协调所有分布式原子事务参与者,并决定提交或取消(回滚)的分布式算法。 在两阶段提交协议中,系统一般包含两类机器(或节点):一类为协调者(coordinator),通常一个系统中只有一个;另一类为事务参与者(participants,cohorts或workers),一般包含多个,在数据存储系统中可以理解为数据副本的个数。协议中假设每个节点都会记录写前日志(write-ahead log)并持久性存储,即使节点发生故障日志也不会丢失。协议中同时假设节点不会发生永久性故障而且任意两个节点都可以互相通信。 请求阶段(commit-request phase,或称表决阶段,voting phase) :在请求阶段,协调者将通知事务参与者准备提交或取消事务,然后进入表决过程。在表决过程中,参与者将告知协调者自己的决策:同意(事务参与者本地作业执行成功)或取消(本地作业执行故障)。 提交阶段(commit phase) :在该阶段,协调者将基于第一个阶段的投票结果进行决策:提交或取消。 当且仅当所有的参与者同意提交事务协调者才通知所有的参与者提交事务,否则协调者将通知所有的参与者取消事务。参与者在接收到协调者发来的消息后将执行响应的操作。 两阶段提交的缺点 同步阻塞问题:执行过程中,所有参与节点都是事务阻塞型的。当参与者占有公共资源时,其他第三方节点访问公共资源不得不处于阻塞状态。 单点故障:由于协调者的重要性,一旦协调者发生故障。参与者会一直阻塞下去。尤其在第二阶段,协调者发生故障,那么所有的参与者还都处于锁定事务资源的状态中,而无法继续完成事务操作。(如果是协调者挂掉,可以重新选举一个协调者,但是无法解决因为协调者宕机导致的参与者处于阻塞状态的问题) 数据不一致:在二阶段提交的阶段二中,当协调者向参与者发送commit请求之后,发生了局部网络异常或者在发送commit请求过程中协调者发生了故障,这回导致只有一部分参与者接受到了commit请求。而在这部分参与者接到commit请求之后就会执行commit操作。但是其他部分未接到commit请求的机器则无法执行事务提交。于是整个分布式系统便出现了数据不一致性的现象。 三阶段提交协议 三阶段提交协议在协调者和参与者中都引入超时机制,并且把两阶段提交协议的第一个阶段拆分成了两步:询问,然后再锁资源,最后真正提交。 CanCommit阶段: 3PC的CanCommit阶段其实和2PC的准备阶段很像。 协调者向参与者发送commit请求,参与者如果可以提交就返回Yes响应,否则返回No响应。 PreCommit阶段: Coordinator根据Cohort的反应情况来决定是否可以继续事务的PreCommit操作。 根据响应情况,有以下两种可能: DoCommit阶段:该阶段进行真正的事务提交 三阶段提交协议和两阶段提交协议的不同 对于协调者(Coordinator)和参与者(Cohort)都设置了超时机制(在2PC中,只有协调者拥有超时机制,即如果在一定时间内没有收到cohort的消息则默认失败)。在2PC的准备阶段和提交阶段之间,插入预提交阶段,使3PC拥有CanCommit、PreCommit、DoCommit三个阶段。PreCommit是一个缓冲,保证了在最后提交阶段之前各参与节点的状态是一致的。

补偿事务(TCC) TCC 是服务化的两阶段编程模型,每个业务服务都必须实现 Try,Confirm,Cancel 三个方法,这三个方式可以对应到 SQL 事务中 Lock,Commit,Rollback。 相比两阶段提交,TCC 解决了几个问题:同步阻塞,引入了超时机制,超时后进行补偿,并不会像两阶段提交锁定了整个资源,将资源转换为业务逻辑形式,粒度变小。 Try 阶段:Try 只是一个初步的操作,进行初步的确认,它的主要职责是完成所有业务的检查,预留业务资源。 Confirm 阶段:Confirm 是在 Try 阶段检查执行完毕后,继续执行的确认操作,必须满足幂等性操作,如果 Confirm 中执行失败,会有事务协调器触发不断的执行,直到满足为止。 Cancel 是取消执行:在 Try 没通过并释放掉 Try 阶段预留的资源,也必须满足幂等性,跟 Confirm 一样有可能被不断执行。 Saga Saga 并不是一个新概念,其相关论文在 1987 年就发布了,和 XA 两阶段提交规范出现的时间差不多。Saga 和 TCC 一样,也是一种补偿事务,但是它没有 try 阶段,而是把分布式事务看作一组本地事务构成的事务链。事务链中的每一个正向事务操作,都对应一个可逆的事务操作。Saga 事务协调器负责按照顺序执行事务链中的分支事务,分支事务执行完毕,即释放资源。如果某个分支事务失败了,则按照反方向执行事务补偿操作。

虽然 Saga 和 TCC 都是补偿事务,但是由于提交阶段不同,所以两者也是有不同的: Saga 是不完美补偿,补偿操作会留下之前原始事务操作的痕迹,需要考虑对业务上的影响。 TCC 是完美补偿,补偿操作会彻底清理之前的原始事务操作,用户是感知不到事务取消之前的状态信息的。 TCC 的事务可以更好的支持异步化,但是 Saga 模式一般在补偿阶段比较适合异步化。 Saga 模式非常适合于业务流程长的长事务的场景,实现上对业务侵入低,所以非常适合微服务架构的场景。同时 Saga 采用的是一阶段提交模式,不会对资源长时间加锁,不存在“木桶效应”,所以采用这种模式架构的系统性能高、吞吐高。 阿里巴巴的 Seata 开源项目和华为的 ServiceComb 开源项目都支持 Saga 模式。 MQ 事务 RocketMQ 在 4.3 版本已经正式宣布支持分布式事务,在选择 RokcetMQ 做分布式事务请务必选择 4.3 以上的版本。 基于消息的分布式事务模式对 ACID 特性的支持如下: 原子性:最终可以实现分支事务都执行或者都不执行。 一致性:提供最终一致性。 隔离性:不保障隔离性。 持久性:由本地事务来保证。 基于消息的分布式事务可以将分布式系统之间更有效的解耦,各个事务参与方之间的调用不再是同步调用。 对 MQ 系统的要求较高,对业务实现也有一定的侵入性,要么提供事务消息状态查询接口,要么需要维护本地消息表。并且原则上只接受下游分支事务的成功,不接受事务的回滚,如果失败就要一直重试,适用于对最终一致性敏感度较低的业务场景,例如跨企业的系统间的调用,适用的场景有限。

最大努力通知型分布式事务 最大努力通知型的分布式事务解决方案,也是基于 MQ 系统的一种解决方案,但是不要求 MQ 消息可靠。 举例 假设小明通过联通的网上营业厅为手机充话费,充值方式选择支付宝支付。整个操作的流程如下: 小明选择充值金额“50 元”,支付方式“支付宝”。 联通网上营业厅创建一个充值订单,状态为“支付中”,并跳转到支付宝的支付页面(此时进入了支付宝的系统中)。 支付宝验明确认小明的支付后,从小明的账户中扣除 50 元,并向联通的账户中增加 50 元。执行完毕后向 MQ 系统发送一条消息,消息的内容标识支付是否成功,消息发送允许失败。 如果消息发送成功,那么支付宝的通知服务会订阅到该消息,并调用联通的接口通知本次支付的结果。如果此时联通的服务挂掉了,导致通知失败了,则会按照 5min、10min、30min、1h、…、24h 等递增的时间间隔,间隔性重复调用联通的接口,直到调用成功或者达到预订的时间窗口上限后,则不再通知。这就是尽最大努力通知的含义。 如果联通服务恢复正常,收到了支付宝的通知,如果支付成功,则给账户充值;如果支付失败,则取消充值。执行完毕后给支付宝通知服务确认响应,确认响应允许失败,支付宝系统会继续重试。所以联通的充值接口需要保持幂等性。 如果联通服务故障时间很久,恢复正常后,已超出支付宝通知服务的时间窗口,则联通扫描“支付中”的订单,主动向支付宝发起请求,核验订单的支付结果。 特点剖析 最大努力通知型方案本质是通过引入定期校验机制来对最终一致性做兜底,对业务侵入性较低、对 MQ 系统要求较低,实现比较简单,适合于对最终一致性敏感度比较低、业务链路较短的场景,比如跨平台、跨企业的系统间的业务交互。

分布式事务中间件 阿里巴巴有两个分布式事务中间件可选择: 蚂蚁金服团队开发的 XTS,金融云产品名称为 DTX。 阿里巴巴中间件团队开发的 TXC。 XTS 和 TXC 的功能差不多,都支持 TCC 事务模式,也都提供了对业务入侵度较低的分布式事务方案,目前这两个团队应该是在共建开源版的分布式事务中间件 Seata。此处我们介绍一下 Seata。 Seata 支持 TCC 模式、Saga 模式。但是 Seata 对 TCC 模式的支持提供了一种对业务入侵度为0的解决方案,这种方案叫做 AT (Automatic Transaction) 模式。下面我们重点说一下 AT 模式的运行机制: 全局事务依然是基于各个分支事务来完成。Seata Server 协调各个分支事务要么一起提交,要么一起回滚。 各个分支事务在运行时,Seata Client 通过对 SQL 执行的代理和拦截,通过解析 SQL 定位到行记录,记录下 SQL 执行前后的行数据快照,beforeImage 和 afterImage 共同构成了回滚日志,回滚日志记录在独立的表中。回滚日志的写入和业务数据的更改在在同一个本地事务中提交。 分支事务完成后,立即释放对本地资源的锁,然后给 Seata 协调器上报事务执行的结果。 Seata 协调器汇总各个分支事务的完成情况,生成事务提交或者回滚的决议,将决议下发给 Seata Client。 如果决议是提交事务,则 Seata Client 异步清理回滚日志;如果决议是回滚事务,则 Seata Client 根据回滚日志进行补偿操作,补偿前会对比当前数据快照和 afterImage 是否一致,如果不一致则回滚失败,需要人工介入。 AT 模式通过自动生成回滚日志的方式,使得业务方接入成本低,对业务入侵度很低,但是应用 AT 模式也有一些限制: AT 模式只支持基于 ACID 事务的关系数据库。 AT 模式是通过对 SQL 解析来完成的,对 SQL 语法的支持有限,使用复杂 SQL 时需要考虑兼容性。 目前不支持复合主键,业务表在设计时注意添加自增主键。 全局事务默认的隔离级别是读未提交,但是通过 SELECT…FOR UPDATE 等语句,可以实现读已提交的隔离级别。通过全局排它写锁,可以做到的隔离级别介于读未提交和读已提交之间。

分布式事务总结 单体数据库事务很容易满足事务的 ACID 四个特性,提供强一致性保证,但是分布式事务要完全遵循 ACID 特性会比较困难。为了追求分布式系统的高可用和高吞吐,分布式事务的解决方案一般提供的是最终一致性。 我们把提供强一致性的事务称之为刚性事务,把提供最终一致性的事务称之为柔性事务。刚性事务可以完全满足 ACID 四个特性,柔性事务对事务的 ACID 特性的支持情况如下: 原子性:完全支持。 一致性:只提供最终一致性支持。 隔离性:不完全保证,通常为了系统的吞吐和性能,会一定程度上放弃对隔离性的要求。 持久性:完全支持。 柔性事务一般遵循的是分布式领域中的 BASE 理论: BA:Basic Availability,基本业务可用性。 S:Soft state,柔性状态。 E:Eventual consistency,最终一致性。 BASE 理论,是对 CAP 理论的延伸,是对 CAP 中的一致性和可用性进行一个权衡的结果,理论的核心思想就是:我们无法做到强一致,但每个应用都可以根据自身的业务特点,采用适当的方式来使系统达到最终一致性。 CAP 理论告诉我们一个分布式系统无法同时满足一致性, 可用性, 分区容错性,所以在设计上对这三点做取舍。刚性事务追求强一致性,所以牺牲了高可用性;柔性事务通过牺牲一致性换来了系统的高可用性。

cap总结 在微服务的构建中,永远都逃离不了 CAP 理论,因为网络永远不稳定,硬件总会老化,软件可能出现 Bug,所以分区容错性在微服务中是躲不过的命题。 可以这么说,只要是分布式,只要是集群都面临着 AP 或者 CP 的选择,但你很贪心的时候,既要一致性又要可用性,那只能对一致性作出一点妥协,也就是引入了 BASE 理论,在业务允许的情况下实现最终一致性。 究竟是选 CA 还是选 CP,真的在于对业务的了解,例如金钱,库存相关会优先考虑 CP 模型,例如社区发帖相关可以优先选择 AP 模型,这个说白了基于对业务的了解是一个选择和妥协的过程。



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有