一、Redis分布式锁

我们可以通过setnx命令简单的实现一个分布式锁。
但是当运行出现异常时,分布式锁会一直存在,就会导致所有线程都卡死在这里,可以使用try-catch-finally包裹业务逻辑,在finally块释放分布式锁。
但是当服务宕机时,分布式锁可能会一直存在,就会导致所有线程都卡死在这里,这种情况可以给分布式锁添加过期时间处理。
但是时间要怎么选,如果设置的时间短了,可能会导致业务逻辑还没执行完就过期了,这时其他线程就可以获取到分布式锁执行业务逻辑,然后之前之前的线程执行完之后,又把第二个进来的分布式锁移除了,让其他的服务又获取到分布式锁,最后导致分布式锁形同虚设。
针对释放其他的服务的分布式锁的问题,可以设置一个识别是自己的值,可以是一个uuid,在删除前判断一下是否是自己加的锁,是自己的锁才会释放锁。但是这么多操作不是原子性,也有可能导致错误释放锁,这可以通过引入lua脚本解决,redis对lua脚本的处理是原子性的,因为redis对命令的处理是单线程的。
另外就是超时问题的处理,可以设置一个足够长的值,来保证一定会完成,但是如果太长,宕机时又会导致其他服务都卡死在这里,这个时候接需要使用看门狗机制,在主线程执行业务逻辑的同时,让一个子线程不断给锁续期,直到主线程执行完业务逻辑,然后才会释放锁,redis对此有专门的Redisson来处理,使用起来很简单。
但是这是单机情况下,如果是主从情况下,如果主节点宕机,但是从节点还没有同步到对应的锁,就会导致有其他的服务获取到锁,这种情况下可以考虑redlock,部署n个独立的redis实例,这些实例之间没有复制关系,没有主从或者集群的分片,每一个实例都是一个孤岛,一般选择奇数个数,推荐使用5个,加锁流程如下:

  1. 客户端记录当前时间T1,然后向5台redis发起加锁请求
  2. 如果大多数即>=3个成功加锁,且加锁耗时小于锁的有效时间才算加锁成功。
  3. 如果锁获取成功,锁的真正有效时间 = 初始 TTL - 获取锁总耗时。
  4. 如果锁获取失败,客户端会向所有 Redis 实例发起释放锁的请求。
    但是redlock成本较高,使用锁的延迟也更高,如果绝对的安全性是首要目标,或许应该考虑 ZooKeeper这类为强一致性而设计的协调系统来实现分布式锁。它们通过 Zab/Raft 协议保证了状态变更的线性一致性,天生避免了脑裂问题,但通常性能低于 Redis。

总之,针对不同的场景,要选择合适的解决方案,一般的场景使用Redisson就够了。

二、ZooKeeper 怎么实现分布式锁

更多推荐