分布式一系列问题的解决方案总结

要使用分布式锁满足的场景有哪些?

1. 系统需要是一个分布式系统,Java 的锁已经锁不住了。
2. 同步访问共享资源,比如数据库里唯一的用户。

分布式锁在实际项目中使用的案例是什么?

用户在消费后会获取消费积分,消费积分可以兑换商品
有两个事件:兑换积分和发放积分。

  • 事件 A 兑换积分:
    用户发起兑换积分请求
    系统查询用户的可用积分是否符合抵扣规则
    开始抵扣
  • 事件 B 发放积分:
    计算用户当天应得积分数
    获取到用户当前积分
    增加积分
    假如:在同一时刻,用户减积分和加积分的操作正好同时进行。
    如果不加锁就会出现:事件A 的操作1 获取当前积分为1000,此时事件B 获取到当前积分为1000,事件A 进行减去999,事件B 增加100为1100,最后得到的用户积分为1100. 正确的应该是 101.
    这种情况是在用户足够多,并发足够大,是会产生的。

Java 提供了 Object 类的 synchornized 锁和 JDK 的 Lock 锁,在单机应用可以使用来避免出现线程不安全的情况。但是这些锁在分布式环境无法解决问题。因为代码都是部署在多台机器上,所以要使用分布式锁。特点是多进程环境下。常见的解决方案是 Redis 和 zookeeper 的分布式锁。

常见的分布式锁有哪些?

Redis 分布式锁
ZK 分布式锁
基于mysql 的分布式锁

Redis 分布式锁是如何实现的?

使用 setNX 命令加锁
long result = setnx(key, requestId );
将key 放入Redis ,如果不存在就缓存起来,返回1,如果返回不是1,表示别的线程已经设置过了
如果等1,在设置过期时间

Redis 锁如果第一步执行完毕,产生异常,第二步没有设置过期时间,如何解决?

将setnx 和 setexpire 合并
set(key, requestId , ‘NX’, ‘px’, 过期时间)

Redis 锁的解锁操作是如何进行的?

释放锁就是删除锁
使用del 命令
使用requestId 判断是否是同一个客户端,然后释放锁
if(requestId.equals( jedis.get(key) )){
jedis.del(key);
}
如果此时if判断进来,还没开始执行del 操作,key 已经失效,那么有可能
会删掉其他的机器上的锁。如果这里没有复杂且耗时业务逻辑,那么正常情况下是没有问题的,极端情况下会出现问题。
如何解决?
使用lua
String script = “if redis.call(‘get’, KEYS[1]) == ARGV[1] then return redis.call(‘del’, KEYS[1]) else return 0 end”;
Object result = jedis.eval(script, Collections.singletonList(lockKey), Collections.singletonList(requestId));
if (1.equals(result)) {
return true;
}
return false;
通过 jedis 客户端的 eval 方法和 script 脚本一行代码搞定,解决方法中的原子问题。

zk 分布式锁是如何实现的?

业务场景:
有积分消费和积分累加的操作,可能出现同时进行。事件A事件B分别是对节分操作的,两台机器同时进行。正确的业务逻辑是:让一台机器执行完,另外一台机器再执行。要么逻辑A先执行,要么逻辑B先执行。

zk 有一种名为临时节点的节点,临时节点有各个客户端创建
当客户端与zk集群断开连接后,则该节点自动被删除
1.客户端调用create 方法创建临时顺序节点
2.客户端调用getChildren() 来获取所有已经创建好的子节点
3.客户端获取到所有子节点的path后,如果发现自己创建的节点是所有节点顺序最小的,那么就认为当前客户端获取了锁,如果不是最小的,那么监视比自己节点小的,进入等待,直到发现自己的节点是最小的,在进行获取锁
释放锁的过程就是删除自己创建的那个节点。不过仍需要考虑删除失败的情况。

Redis 分布式锁和 ZK 分布式锁有什么区别?

Redis 是基于内存的,性能好一点,但是不能保证准确性,因为Redis 集群一般是读写分离的,存在主从不同延迟的情况,可能会出现丢失锁的情况。
所以强一致性要求的业务最好使用 zk 分布式锁
zk 锁,是基于zk 临时顺序节点,临时节点的生命周期在客户端和集群session结束时结束,如果客户端存在网络问题,连接被错误断开 ,session 超时,也会出现锁被错误的释放,也不能保证完全一致性的要求
但是zk 稳定性强。
但是以上所说的极端情况几率很小。

如何利用数据库实现分布式锁?

通过主键Id 的唯一性加锁
在数据库创建一张表,插入一条数据,表中包含方法名类名等字段,id 就是一把锁,比如:当一次请求插入了一条id 为1 的数据,那么其他请求过来,发现存在这条记录,就会等待,知道这条记录被删除。

有哪些比较流行的分布式锁实现?

京东 - 基于Redis 的setnx 的sharkLock
netflux - qrator 基于zk的封装

分布式事务是什么?

比如有一个下单系统,下单完成有优惠券、发短信等,这些子系统的事务需要做到要么一起成功,要么一起失败。

解决方案有:

2pc

3pc

tcc

RocketMQ

2PC实现分布式事务是怎样的?

二阶段提交,是一种强一致性设计
二阶段分别是准备和提交两个阶段

第一阶段:协调者发起准备的命令,客户端响应是否可以提交,并执行本地事务,但不提交

第二阶段:协调者根据响应结果,发起提交或者回滚或者终止
存在单点故障的情况,在极端情况下存在数据不一致情况,事务提交之前会造成阻塞

3PC 实现分布式事务是如何实现的?

3PC 是为了解决2pc的问题,加入了超时机制,引入了第三阶段

第一阶段:协调者询问是否可以执行事务。

第二阶段:执行本地事务,但不提交

第三阶段:根据结果执行提交或者回滚

包含准备阶段,预提交,提交阶段

TCC 如何实现分布式事务?

TCC是业务层面的解决方案,叫做补偿事务,对每个做操都要做确认和补偿
比如:发送短信业务
TCC指的是 try , confirm ,concle
try 指的是预留和锁定资源
confirm 确认
concle 如果执行有问题做处理

缺点是:对代码侵入强,confirm 和 cancle接口还需要实现幂等性

分布式事务如何保证消息最终一致性?

利用 RocketMQ 的支持消息事务的特性

  1. 生产者发送消息给服务端,服务端持久化消息,发送ack给生产者,此时消息是半消息,
  2. 生产者执行本地事务逻辑,向服务端发送执行结果,Commit 或者 Rollback, 如果是 Commit ,
    服务端会将半消息标记为可投递,订阅方最终收到该消息。如果是 Rollback,会删除半消息,消费者不会收到该消息
  3. 在断网或者重启时,二次确认没有到达服务端,经过一段时间后,服务端会发起消息回查
  4. 生产者收到消息会查,会检查对应消息在本地执行结果,重新发起二次确认给服务端。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值