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();
}
03-28 11:13
查看更多