分布式锁方案和缺陷

分布式锁使用场景

  • 解决业务层幂等性,防止双次点击(譬如更新接口)
  • 解决 MQ 消费端多端接受同一消息时保证只有一端处理消息
  • 使用 schedule 执行定时任务时,多实例部署时只有一台实例执行任务

Redis

特点

  • 单线程串行处理
  • 获取锁性能特别好
  • setnx 不存在则设置成功否则失败
  • 没有心跳机制,需要设置失效时间
  • CAP 中的 AP 模型,因为用的是 gossip 协议,所以不是强一致性

多个业务获取锁场景

锁失效时间设置问题

锁定了10s后过期,但业务执行了30s(可能碰到fullgc,死循环等场景)。

主从切换问题

业务1从主获取锁,此时主挂机了,从晋升为主,恰好此时从未同步这个锁的值 。

Zookeeper

特点

  • 有序节点,按排序命名节点
  • 临时节点,客户端断连后自动消失
  • 事件监听,节点下发生更新时会有事件通知
  • ZAB 协议,强一致,属于 CP 模型
  • zk 集群变大后,性能持续下降

多个业务获取锁场景

客户端挂掉或假死

客户端断连会把临时节点删除,锁也就随着释放。另一个业务即可获取锁。
但其实客户端没挂,只是心跳维持间断了。原因有好多,譬如fullgc,网络问题(redis碰到网络问题最多获取锁失败)等。

Etcd

特点

  • 如果存在 Key 的话就不能写入,也就意味着不能获取到锁,如果集群中,可以写入 Key,就意味着获取得到锁。
  • Raft 保证了集群的一致性,强一致性,并且数据是可以进行持久化
  • 没有心跳机制,需要设置失效时间

多个业务获取锁场景

锁失效时间设置问题

锁定了10s后过期,但业务执行了30s(可能碰到fullgc,死循环等场景)。

使用失效时间的锁时间续租问题

在获取到锁的业务线程,可以开启一个子线程去维护和轮训这把锁的有效时间,并定时的对这把锁进行续租。
假设业务线程获取到一把锁,锁的 Expire 时间为 10s,业务线程会开启一个子线程通过轮训的方式每 2 秒钟去把这把锁进行续租,每次都将锁的 Expire 还原到 10s。

存在的问题

  • 业务死循环
  • 业务 fullgc

这些问题都会导致续租线程无法执行,从而导致锁提前失效。

 

posted @ 2019-06-24 12:59  wade&luffy  阅读(...)  评论(... 编辑 收藏