引入redisson的背景
- 由于redis与mysql会出现数据不一致的情况,在更新mysql数据与更新redis数据存在时间差,不同线程的时间差可能不同,例子:A线程更新mysql数据为2,此时A线程卡顿,然后B线程更新mysql数据为3,B线程更新redis数据为3,之后A线程恢复执行,A线程更新redis数据为2;此时出现数据不一致问题,即使:mysql数据为3,redis数据为2
- 针对redis与mysql数据不一致问题;根据需求及并发量,有不同的解决方案。比如:设置数据的过期时间,延迟双删,写多读多直接不使用redis
- 延迟双删,对于需要延迟的时间,需要设置的比业务代码执行的时间长,但是业务代码执行的时间是不定的,延迟时间设置的过长过短均会出现问题。因此可以采用分布式锁redisson
- 在不考虑主从高可用的情况下,可用采用redisson分布式锁,将并行的代码,变成串行,即可解决数据不一致问题。例子:A线程更新mysql数据,更新redis数据执行完毕后,B线程再执行;此时不会出现数据不一致问题
- redisson除解决数据不一致问题,还可以做为分布式锁;如果读多写少,可以使用redisson的读写锁优化性能
项目中引入redisson
- 项目已经引入redis
- pom中引入redisson3.15.0依赖
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.15.0</version>
</dependency>
Lock lock = redissonClient.getLock("lock");
lock.lock();
try {
Thread.sleep(1000);
} catch (Exception e) {
log.info("异常, {}", e.getMessage());
throw new RuntimeException(e);
} finally {
lock.unlock();
}
redisson相关问题
-- 若锁不存在:则新增锁,并设置锁重入计数为1、设置锁过期时间
if (redis.call('exists', KEYS[1]) == 0) then
redis.call('hset', KEYS[1], ARGV[2], 1);
redis.call('pexpire', KEYS[1], ARGV[1]);
return nil;
end;
-- 若锁存在,且唯一标识也匹配:则表明当前加锁请求为锁重入请求,故锁重入计数+1,并再次设置锁过期时间
if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) then
redis.call('hincrby', KEYS[1], ARGV[2], 1);
redis.call('pexpire', KEYS[1], ARGV[1]);
return nil;
end;