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.主要别人建立一把分布式锁,那你就得不断轮询去获取锁。
Config config1 = new Config();
config1.useSingleServer().setAddress("redis://192.168.0.1:5378")
.setPassword("a123456").setDatabase(0);
RedissonClient redissonClient1 = Redisson.create(config1);
Config config2 = new Config();
config2.useSingleServer().setAddress("redis://192.168.0.1:5379")
.setPassword("a123456").setDatabase(0);
RedissonClient redissonClient2 = Redisson.create(config2);
Config config3 = new Config();
config3.useSingleServer().setAddress("redis://192.168.0.1:5380")
.setPassword("a123456").setDatabase(0);
RedissonClient redissonClient3 = Redisson.create(config3);
String resourceName = "REDLOCK_KEY";
RLock lock1 = redissonClient1.getLock(resourceName);
RLock lock2 = redissonClient2.getLock(resourceName);
RLock lock3 = redissonClient3.getLock(resourceName);
// 向3个redis实例尝试加锁
RedissonRedLock redLock = new RedissonRedLock(lock1, lock2, lock3);
boolean isLock;
try {
// isLock = redLock.tryLock();
// 500ms拿不到锁, 就认为获取锁失败。10000ms即10s是锁失效时间。
isLock = redLock.tryLock(500, 10000, TimeUnit.MILLISECONDS);
System.out.println("isLock = "+isLock);
if (isLock) {
//TODO if get lock success, do something;
}
} catch (Exception e) {
} finally {
// 无论如何, 最后都要解锁
redLock.unlock();
}