Redis_Redisson_Redlock

一,redis锁的普通实现
设置key(myKey)的值为myUuid(当前线程唯一标志值),NX代表key不存在才设置,PX代表30秒后过期
set myKey myUuid NX PX 30000
使用lua脚本执行,保证原子性,判断key对应的值是当前线程的myUuid才删除,避免由于当前线程阻塞超过30秒后再删除key时,误删了其他线程设置的key。
if redis.call('get',myKey) == myUuid then
    return redis.call('del',myKey)
else
    return 0
end

二,Redisson对redis锁的实现
首先当前线程获取一个uuid,再获取一个线程id,然后两者拼起来uuid:threadId作为当前线程特殊标志,
假设要加锁的key是myKey,key过期时间是myTime,

1.则获取锁时,发送以下lua脚本导redis上:
//加锁
if (redis.call('exists', myKey) == 0) then        //如果myKey不存在,则
  redis.call('hset', myKey, uuid:threadId, 1);    //设置myKey的值为{"uuid:threadId": 1},key是map类型
  redis.call('pexpire', myKey, myTime);           //设置myKey的过期时间为myTime
  return nil;
end;
//实现可重入锁
if (redis.call('hexists', myKey, uuid:threadId) == 1) then //如果myKey已存在,且存在uuid:threadId这个值,代表是当前线程加的锁,则
  redis.call('hincrby', myKey, uuid:threadId, 1);          //重入次数加1
  redis.call('pexpire', myKey, myTime);                    //重新设置myKey的过期时间为myTime
  return nil;
end;
//返回myKey的失效时间毫秒数
return redis.call('pttl', myKey);

2.解锁时,发送以下lua脚本导redis上:
if (redis.call('exists', myKey) == 0) then             //如果myKey不存在,则
    redis.call('publish', channelName, unlockMessage); //发送解锁信息到channel上
    return 1;
end;
if (redis.call('hexists', myKey, uuid:threadId) == 0) then //myKey已经存在,但不是当前线程拥有的,则直接返回
    return nil;
end;
//走到这步,表示myKey是当前线程拥有的,则将重入次数-1
local counter = redis.call('hincrby', myKey, uuid:threadId, -1);
if (counter > 0) then                        //重入次数减1后的值>0,则
    redis.call('pexpire', myKey, myNewTime); //重新设置超时时间
    return 0;
else
    redis.call('del', KEYS[1]);              //重入次数减1后的值=0,则删除myKey
    redis.call('publish', channelName, unlockMessage);//发送解锁信息到channel上
    return 1;
end;
return nil;

3.使用Redisson代码加锁解锁:
RLock lock = redisson.getLock("myKey");
lock.lock();
//执行业务
lock.lock();//可重入锁
lock.unlock();//解锁
lock.unlock();//解锁

三,以上实现只能在单机上运行,一旦redis有主从结构,会出现问题:
1.客户端A从master中获取锁成功,master将数据同步给slave时发生了故障,slave没同步到锁相关数据。
2.slave升级为master,其他客户端可以它上面获取锁,但此时客户端A还保留着锁。

四,但是在单机上运行一旦发生单点故障则所有锁都不可用了,如需要在多台机器上实现redis锁,保障高可用,可用Redlock算法
1.需要有N个redis单位,这个单位可以是redis单机或redis集群(集群是主从模式,哨兵模式或cluster模式都行),单位与单位之间没有任何关系
2.客户端获取redis锁时,依次从这几个单位获取锁,当获取锁数量大于等于(N/2+1)个,且使用时间小于锁超时时间,则获取锁成功。
3.获取锁成功后,锁实际超时时间 = 设置的超时时间 - 获取锁花费时间
4.如获取锁失败,比如获取数量过少或获取花费时间大于锁超时时间,则客户端在所有redis单位上都解锁
  (有可能在redis单位上加锁成功了,但没响应给客户端,客户端不知道加锁成功了,因此要对所有单位都解锁)

五,Redisson整合了Redlock算法,相关代码如下:
//lock1, lock2, lock3中分别配置了3个redis单位的信息
RedissonRedLock redLock = new RedissonRedLock(lock1, lock2, lock3);
try {
    //1000ms获取不到锁则返回获取失败,10000ms是锁失效时间
    if (redLock.tryLock(1000, 10000, TimeUnit.MILLISECONDS)) {
        //执行业务
    }
} catch (Exception e) {
} finally {
    redLock.unlock();//解锁
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值