Redis锁 - RedLock

RedLock 由来原理

redis提供了一个分布式锁的规范算法 Redlock java版本:Redisson :https://github.com/redisson/redisson

锁的特点:独享(互斥)、无死锁(持有锁的客户端崩溃或者网络分裂,锁仍然可以使用)、容错(大部分节点活着可用)

第一个阶段:

redis设置锁,就是创建一个key,然后针对整个key设置过期时间,执行完,删除这个key,这里面有个问题,redis挂了怎么办?增加一个从节点?redis的主从是异步的,如果主挂了,还是有问题。(客户端A从master获取到锁,然后master挂了,slave成为主,这时候客户端B获取到锁,锁失效)

第二个阶段:

使用单个redis实例上锁 这种方式保证redis在大多数场景下总是可用的

使用redis的特性setnx

SET resource_name my_random_value NX PX 30000

只有key存在才能设置成功,多个客户端竞争的key resource_name ,每个客户端的my_random_value不能相同,30000 过期时长,PX过期属性

之后执行完业务逻辑,删除我自己创建的锁,使用lua脚本,执行是原子性,这里要校验一下存的值value和当前客户端存放的值相同,才能删除,防止删了别人的锁,这个my_random_value一定要是唯一的,

if redis.call("get",KEYS[1]) == ARGV[1] then return redis.call("del",KEYS[1]) else return 0 end

Redlock算法

在redis的分布式环境中,假设有N个Master节点,彼此之间是独立的,通过同时操作多台加锁解决,现在redis需要加锁,就在这N个节点执行加锁或者释放锁的时间,具体操作和上面单个redis处理一样,具体执行的步骤如下:

1.获取当前的时间,以毫秒为单位;

2.依次或者并发尝试,调用N个实例上使用锁,使用相同的key设置锁,客户端与redis连接超时时间要小于锁的过期时间,不然有的节点锁已经过期了,你还没拿到锁,还在等待是没有意义的。如果锁在规定的时间之内没有获取到锁,就尝试另外一台redis;

3.客户端使用时间为当前时间,减去开始获取锁的时间,就是获取锁的使用时间t1,当且仅当这里的大多数redis获取到锁(至少是N/2+1,过半),同时t1<过期时间,获取锁成功。

4.获取锁成功,那么业务执行的时间为过期时间减t1,就是真正执行的时间;(这里要注意,锁的失效时间和业务执行时间之间的关系)

5.如果获取锁失败(获取锁超时或者获取锁的数量少于N/2+1),客户端需要释放锁。

失败重试

多个客户端,同时获取锁都获取到部分,没有过半,则生成一个随机等待的时间,之后再次执行获取锁,还有获取锁失败的客户端要尽快的释放锁,让其它的尽快获取到,多个客户端获取锁,要并发的请求,获取的速度足够快失败的次数就少一点。

释放锁

向所有节点发送释放指令,不用关心之前时候获得过锁,使用lua脚本

如果我们使用redis锁,aof落盘方式使用fsync=always,提高锁的安全性,但是降低了性能

这里还有一个问题,如果redis挂了,然后重启,这时候可能clientA获取到了锁,clientB也获取到了,这时候我们使用redis的延迟重启(超过这个TTL),保证A锁过期之后再重启成功,可以避免这个问题,但是会有性能上的问题。

产生死锁场景

如果一个业务机器获取到redis锁,在执行的过程中如果业务机器挂了,这时候就会导致锁一直在这个业务机器没有释放,导致死锁。

解决死锁方法 可重入锁

redisson,为了解决这个问题设置一个lockWatchdogTimeout参数,看门狗检查超时时间,默认为30秒这个可以通过Config.lockWatchdogTimeout 参数进行设置,这个作用保证在客户端执行业务过程中,不会因为业务还没有执行完成就过期。如果业务没有执行完,锁快到期不断的进行续期。

另外Redisson还通过加锁的方法,提供leaseTime的参数来指定加锁的时间,如果超过这个时间之后便自动解开。

// 加锁以后10秒钟自动解锁
// 无需调用unlock方法手动解锁
lock.lock(10, TimeUnit.SECONDS);

// 尝试加锁,最多等待100秒,上锁以后10秒自动解锁
boolean res = lock.tryLock(100, 10, TimeUnit.SECONDS);
if (res) {
   try {
     ...
   } finally {
       lock.unlock();
   }
}

RLock对象完全符合Java的Lock规范。也就是说只有拥有锁的进程才能解锁,其他进程解锁则会抛出IllegalMonitorStateException错误。但是如果遇到需要其他进程也能解锁的情况,请使用分布式信号量Semaphore 对象.

红锁(RedLock)

如果因为单个redis节点宕机或者主从的问题导致线程安全的问题,使用redlock解决

基于Redis的Redisson红锁RedissonRedLock对象实现了Redlock介绍的加锁算法。该对象也可以用来将多个RLock对象关联为一个红锁,每个RLock对象实例可以来自于不同的Redisson实例。

RLock lock1 = redissonInstance1.getLock("lock1");
RLock lock2 = redissonInstance2.getLock("lock2");
RLock lock3 = redissonInstance3.getLock("lock3");

RedissonRedLock lock = new RedissonRedLock(lock1, lock2, lock3);
// 同时加锁:lock1 lock2 lock3
// 红锁在大部分节点上加锁成功就算成功。
lock.lock();
...
lock.unlock();

大家都知道,如果负责储存某些分布式锁的某些Redis节点宕机以后,而且这些锁正好处于锁住的状态时,这些锁会出现锁死的状态。为了避免这种情况的发生,Redisson内部提供了一个监控锁的看门狗,它的作用是在Redisson实例被关闭前,不断的延长锁的有效期。默认情况下,看门狗的检查锁的超时时间是30秒钟,也可以通过修改Config.lockWatchdogTimeout来另行指定。

另外Redisson还通过加锁的方法提供了leaseTime的参数来指定加锁的时间。超过这个时间后锁便自动解开了。

RedissonRedLock lock = new RedissonRedLock(lock1, lock2, lock3);
// 给lock1,lock2,lock3加锁,如果没有手动解开的话,10秒钟后将会自动解开
lock.lock(10, TimeUnit.SECONDS);

// 为加锁等待100秒时间,并在加锁成功10秒钟后自动解开
boolean res = lock.tryLock(100, 10, TimeUnit.SECONDS);
...
lock.unlock();

附录:

REDIS distlock -- Redis中国用户组(CRUG)

8. 分布式锁和同步器 · redisson/redisson Wiki · GitHub

汪~汪~汪~redisson的WatchDog是如何看家护院的? - 菜鸟学院

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Redis分布式是一种解决分布式系统并发控制的技术,其中redlock是一种常见的实现方式。它基于Redis的原子操作和特性,可以保证在分布式环境下实现互斥访问。 Redlock算法是由Redis作者Antirez在2016年提出的,它的目标是在多个Redis实例之间实现可靠的分布式Redlock的核心思想是通过在多个Redis实例上创建互斥的,以确保只有一个客户端能够获取到。 下面是Redlock算法的基本步骤: 1. 客户端获取当前时间戳,并尝试在多个Redis实例上创建,每个实例上创建的过程需要设置超时时间和唯一的标识。 2. 客户端计算获取的时间,如果在大多数(例如大于一半)Redis实例上成功获取到,并且获取的时间没有超过的有效时间,那么认为获取成功。 3. 如果获取失败,客户端会尝试在所有Redis实例上释放之前获取到的Redlock算法的关键点在于: - 在多个Redis实例上创建,以增加可靠性。 - 通过获取的时间和的有效时间来评估的可用性。 - 通过释放之前获取到的来保证的互斥性。 需要注意的是,尽管Redlock算法提供了一种可靠的分布式实现方式,但它并不是完美的。在一些特殊情况下,例如网络分区或者时钟偏差等问题,Redlock可能会出现错误。因此,在使用Redlock算法时,需要仔细评估系统的需求和可靠性要求,以及合理地配置和使用Redis实例。 这就是关于Redis分布式Redlock算法的简要介绍,希望能对你有所帮助!如果你有其他问题,请继续提问。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值