分布式锁 redis与zookeeper

redis实现分布式锁

原理

基于redis命令setnx key value来实现分布式锁的功能,只有当key不存在时,setnx才可以设置成功并返回1,否则设置失败返回0。

方案1:
方案1存在的问题

假如在加锁成功,释放锁之前,服务器宕机了,这个锁就无法释放了,其他线程就永远无法获取到锁了。

改进之后

方案2
方案2存在的问题

获取锁和设置过期时间是2个命令,两次网络IO和redis交互,不具备原子性,可能出现获取锁成功之后 设置过期时间之前服务器宕机,导致其他线程永远都获取不到锁的现象。

方案3

1、将获取锁和设置过期时间放在lua脚本里面执行,lua脚本可以保证多个命令原子性的执行。

2、redis提供了其他命令在setnx的同时设置过期时间,分别如下所示:

  • set key value PX 多少毫秒 NX
  • set key value EX 多少秒 NX
方案3存在的问题

锁过期释放了,但是业务仍没有执行完,等到执行完业务并释放锁的时候,释放的其实是其他线程获取到的锁。

  • 线程A获取到了锁,并设置过期时间10s。
  • 当过了10秒后,线程A仍然没有执行完,但是此时线程A获取到的锁已经达到了过期时间,导致锁被释放了。
  • 此时线程B获取到了锁,并执行业务方法
  • 线程A业务方法执行完,并执行释放锁逻辑(此时释放的是线程B获取到的锁)
  • 由于锁被释放,线程C也获取到了锁,并执行业务,此时线程B、线程C都获取到了同一个锁,并执行相应的业务。
方案4
方案4存在的问题

方案4中 判断key1的value1是否等于设置的值uuid,如果是则删除,否则不执行删除逻辑,由于是2个命令,不具备原子性导致可能出现以下场景

  • 线程A成功获取锁key1,设置key1的value为uuid1,并设置过期时间10秒。
  • 线程A执行完业务逻辑,准备删除锁
  • 线程A获取到key1的value uuid1。
  • 此时线程A的锁key1刚好到了过期时间
  • cpu时间片切换,线程A停止执行,线程B开始执行
  • 线程B尝试获取分布式锁key1(由于上面线程A设置的锁已经到了过期时间,所以此处可以获取成功)
  • cpu时间片切换,线程A继续执行,由于锁的value == uuid1,所以开始删除锁(此时删除的是线程B设置的分布式锁)。导致出现了删除其他线程设置的分布式锁。
方案五
方案5存在的问题

当前仍存在锁时间达到了过期时间,但是线程没执行完的问题。

如何解决呢?

watch dog机制(Redisson底层就是基于watch dog机制来实现分布式锁的自动续期,当未设置过期时间时,会默认设置30秒,并起一个看门狗线程,每十秒去检测一次,如果当前线程仍然在执行,就自动续期,当然,如果手动设置了过期时间,就不会自动续期了)。

方案五存在的问题

redis集群的场景下,redis-master节点设置成功了,但是redis-slave节点未设置成功,如下所示:

  • 当前redis集群有三个节点,分别是redis-master、redis-slave-a、redis-slave-b
  • 线程A尝试获取分布式锁,此时在redis-master上设置key1成功。
  • redis集群采用异步主从复制,但是redis-master上设置的key内容还没有同步到slave时,redis-master节点就宕机了。
  • 此时集群重新选举,因此redis-slave-a变成了mater节点
  • 此时有线程B尝试获取分布式锁key1,由于新的master节点上没有设置key1,因此线程B设置锁成功。
  • 当前线程A和线程B就都持有了分布式锁并执行业务逻辑。
方案6

如何解决方案五仍然存在的问题呢?

使用redlock(红锁)算法,这是一个专门用于解决这种问题的分布式锁协议。

redlock:不能只在一个redis实例上加锁,应该是在多个redis实例上创建锁,当创建成功数量达到(n/2 + 1)时,才能认为加锁成功

以上方式实现复杂、性能差、运维繁琐。

我们的redis是AP高可用思想、如果要实现强一致性,应该使用CP思想的zookeeper来实现,解决主从一致性问题。

C:Consistency;一致性

A:Availabilit;可用性

P:Partition Tolerance;分区容错性

zookeeper实现分布式锁

zookeeper的节点有以下几种类型;

  • 临时节点
    当客户端和zookeeper服务端断开连接时,临时节点就会被删除
  • 临时有序节点
    临时节点且都是有序的排列
  • 持久节点
    当客户端和zookeeper服务端断开连接时,持久节点也不会被删除
  • 持久有序节点
    持久节点且都有序的排列

zookeeper实现分布式锁就是利用到了临时有序节点。

区别

相关推荐
Justice link2 分钟前
企业级NoSql数据库Redis集群
数据库·redis·缓存
爱的叹息3 分钟前
主流数据库的存储引擎/存储机制的详细对比分析,涵盖关系型数据库、NoSQL数据库和分布式数据库
数据库·分布式·nosql
千层冷面1 小时前
RabbitMQ 发送者确认机制详解
分布式·rabbitmq·ruby
ChinaRainbowSea1 小时前
3. RabbitMQ 的(Hello World) 和 RabbitMQ 的(Work Queues)工作队列
java·分布式·后端·rabbitmq·ruby·java-rabbitmq
敖正炀1 小时前
基于RocketMQ的可靠消息最终一致性分布式事务解决方案
分布式
一條狗3 小时前
随笔 20250402 分布式 ID 生成器 Snowflake 里面的坑
分布式
爱的叹息3 小时前
Spring Boot 集成Redis 的Lua脚本详解
spring boot·redis·lua
小马爱打代码3 小时前
Kubernetes 中部署 Ceph,构建高可用分布式存储服务
分布式·ceph·kubernetes
码熔burning3 小时前
【Spring Cloud Alibaba】:Nacos 入门讲解
分布式·spring cloud·微服务
morris13111 小时前
【redis】redis实现分布式锁
数据库·redis·缓存·分布式锁