redis的常见部署方式:
1.单机 单点问题
2.mster-slave 读写分离,不能切换
3.哨兵 哨兵模式 通过哨兵监控节点,主节点宕机自动切换slave为master
4.cluster 基于槽点16348个槽点 多主多从,组合形成
Redis分布式锁要保证的几个方面:
1.互斥性
2.不能死锁
3.大多数节点正常,锁不能丢失。
分布式锁的实现方案:
1.可以直接通过 set key value px milliseconds nx
命令实现加锁, 通过Lua脚本实现解锁
存在的风险,锁所在的节点发生宕机,导致锁会丢失的。
2.Redission 实现
Redission在Redis的基础上实现Java驻内存数据网格。提供了一系列的分布式的Java常用对象,重入锁,读写锁,公平锁,红锁,还提供了很多的分布式服务。Redission提供了使用Redis的最简单和便捷的方式。
/ 1.构造redisson实现分布式锁必要的Config
Config config = new Config();
config.useSingleServer().setAddress("redis://127.0.0.1:5379").setPassword("123456").setDatabase(0);
// 2.构造RedissonClient
RedissonClient redissonClient = Redisson.create(config);
// 3.获取锁对象实例(无法保证是按线程的顺序获取到)
RLock rLock = redissonClient.getLock(lockKey);
try {
/**
* 4.尝试获取锁
* waitTimeout 尝试获取锁的最大等待时间,超过这个值,则认为获取锁失败
* leaseTime 锁的持有时间。
*/
boolean res = rLock.tryLock((long)waitTimeout, (long)leaseTime, TimeUnit.SECONDS);
if (res) {
//成功获得锁,在这里处理业务
}
} catch (Exception e) {
throw new RuntimeException("aquire lock fail");
}finally{
//无论如何, 最后都要解锁
rLock.unlock();
}
获取锁成功就会开启一个定时任务,也就是watchdog
,定时任务会定期检查去续期renewExpirationAsync(threadId),避免业务执行时间大于锁的超时时间,业务没有执行完成锁已经释放。
RedissionLock同样没有解决节点挂掉的问题,存在丢失锁的问题。RedissonRedLock 真正解决了单点失败的问题,代价是需要额外的为 RedissonRedLock 搭建Redis环境。
3.RedissonRedLock 算法
假设一个redis cluster有5个master节点。
1.获取当前时间戳,单位是毫秒
2.和上面set的方式相似,在每个master节点创建锁,过期时间较短,一般几十毫秒。
3.尝试在大多数节点上获取锁,比如总共五个节点就要三个。
4.客户端计算好获取锁的时间,如果客户端获取锁的时间小于超时时间,就算获取成功了。
5.如果锁获取失败,则要依次把之前的锁删掉。
6.主要别人建立一把分布式锁,那你就得不断轮询去获取锁。