分布式CAP漫谈

1.CAP

在经典的CAP理论中:

C: 一致性

A: 高可用

P: 容错性

在目前这个微服务中,注册中心选择中ZK和Eureka比较常见的,

zookeeper在设计之初是为了解决各个服务之间的数据(状态)都保持一致,所以使用zk做注册中心时,会将这一特性带过来,所以大家常说使用zk的服务,而当zk内部发生选举,或者有一半节点挂掉时,没有办法对外提供服务,所以不是高可用性的,是CP.

image

Eureka在设计之初明确就是作为一个注册中心,它主要保证的可用性和容错性,即AP;

数据不一致性在注册服务中中会给eureka带来什么问题,无非就是某一个节点被注册的服务多,某个节点注册的服务少,在某一个瞬间可能导致某些ip节点被调用数少,某些ip节点调用数少的问题。也有可能存在一些本应该被删除而没被删除的脏数据

从注册中心的角度上来说,还是更偏向于高可用的Eureka.

2.分布式锁

目前分布式锁有三种实现方式:

1.基于关系型数据库(mysql)

2.基于redis

3.基于zookeeper

2.1基于数据库

利用数据库中的 UNIQUE KEY作为唯一主键,查询不存在,则insert数据,持有锁,使用完成后,delete该数据,则表示释放锁.

关系数据库谈不上CAP,不属于分布式的范畴内.

2.2基于redis
setnx key value expire_time
//设置成功则获取到锁

在redis集群中,一般使用的是主从+sentinel,一旦主redis出现问题,可以让sentinel做故障迁移,保证可用性.

当集群中的主redis设置了锁成功,然后该redis出现故障,这时还没有同步给从redis,就被sentinel做了故障转移.这时设置的锁,会丢失.故不能保证一致性.

所以说基于redis的分布式锁是AP.

2.3基于zookeeper

zk所具有的特性:

1.有序节点

//在一个 /lock 父节点下
//创建的每个节点都是有序的(如:0001,0002,0003...)

2.临时节点

//创建临时节点,当会话到期,
//客户端不发送心跳时,节点会被自动删除.

3.事件监听

//在读取数据时,我们可以对节点设置监听,当节点的数据发生变化
//(1,节点创建 2,节点删除 3,节点数据变成 4,自节点变成)时,zookeeper会通知客户端

使用zk做分布式锁,就是让多个线程去争抢去特定目录下创建节点,节点号最小的持有锁.

treadA ---> /lock/0001
treadB ---> /lock/0002

//这时treadA获取到锁,
//当treadA使用 完后释放,
//创建 的节点会被自动删除,
//thradB创建的/lock/0002成为了最下的节点,
//这时threadB获取到了锁.

根据zk的特性可以保证集群中不同的节点,数据的一致性,也就是说集群内所有的zk都会记录/lock下的节点信息.保证了一致性.

当时当发生选举,无法集群无法提供服务,则不能保证可用性.

(这个实际上是zk的leader通过二阶段提交写请求来保证的数据一致性,这个也是zk的集群规模大了的一个瓶颈点)

基于zk的分布式锁,具有CP.

2.4 使用场景

再实际的技术选型中,要看实际的业务场景,比如在不是很要求强一致性的业务场景下,比如:点赞,这时可以考虑使用基于redis的分布式锁.

但是当在金融相关的业务中,对数据的一致性要求很高,这个时候,要采用给予zookeeper的分布式锁,来保证强一致性.

有句话说的好,小孩子才做选择,大人是全都要!,如何做到全都要呢.

这就要说到牺牲强一致性,做妥协,实现最终一致性.

3.最终一致性的解决方案

1.两阶段提交(2PC)

2.补偿事务(TCC)

3.本地消息表

4.MQ事务消息

3.1 两段式提交

一般时基于关系型数据库的事务,第一段所有sql提交前获取事务,都获取到后,统一commit,一旦有一个出现问题,则回滚.

这种方式的问题很多,再互联网公司中,高并发的情况下性能太差,网络出现波动对事物提交也有很大的影响.

3.2 补偿事物

TCC是服务化的两阶段变成模型,每个业务服务都必须实现 try,confirm,calcel三个方法,这三个方式可以对应到SQL事务中Lock,Commit,Rollback。

相比两阶段提交,TCC解决了几个问题:

同步阻塞,引入了超时机制,超时后进行补偿,并不会像两阶段提交锁定了整个资源,将资源转换为业务逻辑形式,粒度变小。
因为有了补偿机制,可以由业务活动管理器进行控制,保证数据一致性。

1). try阶段
try只是一个初步的操作,进行初步的确认,它的主要职责是完成所有业务的检查,预留业务资源

2). confirm阶段
confirm是在try阶段检查执行完毕后,继续执行的确认操作,必须满足幂等性操作,如果confirm中执行失败,会有事务协调器触发不断的执行,直到满足为止

3). cancel是取消执行,在try没通过并释放掉try阶段预留的资源,也必须满足幂等性,跟confirm一样有可能被不断执行.

3.3 本地消息表

本地消息表这种实现方式应该是业界使用最多的,其核心思想是将分布式事务拆分成本地事务进行处理。

对于本地消息队列来说,核心就是将大事务转变为小事务,还是用上面下订单扣库存的例子说说明:

image

当我们去创建订单的时候,我们新增一个本地消息表,把创建订单和扣减库存写入到本地消息表,放在同一个事务(依靠数据库本地事务保证一致性)

配置一个定时任务去轮训这个本地事务表,扫描这个本地事务表,把没有发送出去的消息,发送给库存服务,当库存服务收到消息后,会进行减库存,并写入服务器的事务表,更新事务表的状态。

库存服务器通过定时任务或直接通知订单服务,订单服务在本地消息表更新状态。

这里须注意的是,对于一些扫描发送未成功的任务,会进行重新发送,所以必须保证接口的幂等性。

本地消息队列是BASE理论,是最终一致性模型,适用对一致性要求不高的情况。

(https://juejin.im/post/5d720e86f265da03cc08de74)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值