Redis的分布式锁方案Redisson


目录

  1. SETNX
  2. Redisson

参考/来源

SETNX

大家熟知的是使用SETNX命令实现Redis的分布式锁

SETNXset If not exist 的简写。意思就是当 key 不存在时,设置 key 的值,存在时,什么都不做。

set <key> <value> NX

set <key> <value> PX <多少毫秒> NX

set <key> <value> EX <多少秒> NX

各种基于SETNX的Java代码思路大致如下:

// 1.生成唯一 id
String uuid = UUID.randomUUID().toString();
// 2. 抢占锁
Boolean lock = redisTemplate.opsForValue().setIfAbsent("lock", uuid, 10, TimeUnit.SECONDS);
if(lock) {
    System.out.println("抢占成功:" + uuid);
    // 3.抢占成功,执行业务
    List<TypeEntity> typeEntityListFromDb = getDataFromDB();
    // 4.获取当前锁的值
    String lockValue = redisTemplate.opsForValue().get("lock");
    // 5.如果锁的值和设置的值相等,则清理自己的锁
    if(uuid.equals(lockValue)) {
        System.out.println("清理锁:" + lockValue);
        redisTemplate.delete("lock");
    }
    return typeEntityListFromDb;
} else {
    System.out.println("抢占失败,等待锁释放");
    // 4.休眠一段时间
    sleep(100);
    // 5.抢占失败,等待锁释放
    return getTypeEntityListByRedisDistributedLock();
}

Redisson

Redisson 提供了使用 Redis 的最简单和最便捷的方法。Redisson 的宗旨是促进使用者对 Redis 的关注分离(Separation of Concern),从而让使用者能够将精力更集中地放在处理业务逻辑上。

img

下面介绍Springboot整合Redisson的基本使用

依赖

<!-- https://mvnrepository.com/artifact/org.redisson/redisson -->
<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson</artifactId>
    <version>3.15.5</version>
</dependency>

注意,这里不在需要引入redis的依赖即可使用redisson!!!

配置文件

主要进行redis配置

spring:
#redis
   redis:
      host: localhost
      password: 123456
      port: 6379
      pool:
         max-idle: 100
         min-idle: 1
         max-active: 1000
         max-wait: -1

配置类

这里前提应该是要启动Redis,这里省略了相关配置

@Configuration
public class MyRedissonConfig {
    /**
     * 对 Redisson 的使用都是通过 RedissonClient 对象
     * @return
     * @throws IOException
     */
	@Value("${spring.redis.password}")
	private String password;

	@Bean(destroyMethod="shutdown") // 服务停止后调用 shutdown 方法。
    public RedissonClient redisson() throws IOException {
        // 1.创建配置
        Config config = new Config();
        // 集群模式
        // config.useClusterServers().addNodeAddress("127.0.0.1:7004", "127.0.0.1:7001");
        // 2.根据 Config 创建出 RedissonClient 示例。
        config.useSingleServer()
        	.setAddress("redis://127.0.0.1:6379")
        	.setPassword(password);
        return Redisson.create(config);
    }
}

分布式可重入锁

@RestController
public class TestRedisson {
	
	@Autowired
	private RedissonClient redisson;

	@GetMapping("test-lock")
	public String TestLock() {
		// 1.获取锁,只要锁的名字一样,获取到的锁就是同一把锁。
		RLock lock = redisson.getLock("mylock");

		// 2.加锁
		lock.lock();
		try {
			System.out.println("加锁成功,执行后续代码。线程 ID:" + Thread.currentThread().getId());
			Thread.sleep(10000);
		} catch (Exception e) {
			// TODO
		} finally {
			lock.unlock();
			// 3.解锁
			System.out.println("Finally,释放锁成功。线程 ID:" + Thread.currentThread().getId());
		}

		return "test lock ok";
	}
}

为了防止服务宕机导致未释放锁,Redisson 内部提供了一个监控锁的看门狗,它的作用是在 Redisson 实例被关闭前,不断的延长锁的有效期。

如果我们未制定 lock 的超时时间,就使用 30 秒作为看门狗的默认时间。只要占锁成功,就会启动一个定时任务:每隔 10 秒重新给锁设置过期的时间,过期时间为 30 秒。

自定义设置超时时间:

lock.lock(8, TimeUnit.SECONDS);

其他锁

Redis还提供了很多其他的锁形式,如分布式读写锁RReadWriteLock、分布式信号量RSemaphore等等。

RReadWriteLock rwlock = redisson.getReadWriteLock("anyRWLock");
// 10秒钟以后自动解锁
// 无需调用unlock方法手动解锁
rwlock.readLock().lock(10, TimeUnit.SECONDS);
// 或
rwlock.writeLock().lock(10, TimeUnit.SECONDS);

// 尝试加锁,最多等待100秒,上锁以后10秒自动解锁
boolean res = rwlock.readLock().tryLock(100, 10, TimeUnit.SECONDS);
// 或
boolean res = rwlock.writeLock().tryLock(100, 10, TimeUnit.SECONDS);
...
lock.unlock();
// 获取信号量(停车场)
RSemaphore park = redisson.getSemaphore("park");
// 获取一个信号(停车位)
park.acquire();
// 释放一个信号(停车位)
park.release();

文章作者: 小小千千
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 小小千千 !
评论
  目录