redis主从架构
redis哨兵架构
redis的集群架构
Redis的单线程和高性能
redis管道操作(节省网络IO开销)
redis的lua脚本
redis分布式锁
redis分布式锁redisson
redis缓存优化
redis的过期淘汰策略
redis连接池参数
setnx key value
将key的值设为value,仅当key不存在时。
若给定的key已经存在,则setnx不做任何动作。
SETNX 是 set if not exists 的简写
由于redis的IO单线程特性,所以同一时间只会有一个线程设值成功。
1.0版本
)String lockKey = "lock:product_101";
boolean result = stringRedisTemplate.opsForValue().setIfAbsent(lockKey,"value");
if(result){
//业务逻辑
}
//释放锁
stringRedisTemplate.delete(lockKey)
假设业务逻辑里面抛异常,则锁并未得到释放。(2.0版本
)
String lockKey = "lock:product_101";
boolean result = stringRedisTemplate.opsForValue().setIfAbsent(lockKey,"value");
if(result){
try{
//业务逻辑
}catch(Exception e){
}finally{
//释放锁
stringRedisTemplate.delete(lockKey)
}
}
如果业务逻辑执行到一半,宕机了。则锁也不会得到释放
解决办法,在设置锁的时候设置10秒钟的超时时间,到期自动释放 (3.0版本
)
String lockKey = "lock:product_101";
//在设置锁的时候设置10秒钟的超时时间,到期自动释放
boolean result = stringRedisTemplate.opsForValue().setIfAbsent(lockKey,"value",10,TimeUnit.SECONDS);
if(result){
try{
//业务逻辑
}catch(Exception e){
}finally{
//释放锁
stringRedisTemplate.delete(lockKey)
}
}
大多数公司在并发量不是很高的情况下,也在允许小笔的超卖等业务的情况下,该版本适用, 该版本易于维护。
如果在高并发的情况下,短时间大量业务请求进来的时候,程序会变慢,则设置的10秒钟自动释放可能不够用。会存在10秒钟该业务逻辑没有处理完的情况,在该情况下,锁被自动释放了。 这里当其他线程又拿到锁之后,进行业务逻辑处理。当业务逻辑处理到一部分的时候,之前的线程业务逻辑处理完了。并在这个时候把锁释放了。这里相当于A线程把B线程的锁释放了。这样C线程又会加锁成功。这样可能导致了这把锁一直失效,会导致大量的超卖。
解决办法: 将value设置成有标识的id,在释放锁的时候校验该id,属于自己的才释放该锁。(4.0版本
)
这里不能设置成线程id,因为每台服务器都可能有相同的线程id
String lockKey = "lock:product_101";
//在设置锁的时候设置10秒钟的超时时间,到期自动释放
String clientId = UUID.randomUUID().toString();
boolean result = stringRedisTemplate.opsForValue().setIfAbsent(lockKey,clientId,10,TimeUnit.SECONDS);
if(result){
try{
//业务逻辑
}catch(Exception e){
}finally{
//释放锁
if(clientId.equals(stringRedisTemplate.opsForValue().get(lockKey))){
stringRedisTemplate.delete(lockKey)
}
}
}
//释放锁
if(clientId.equals(stringRedisTemplate.opsForValue().get(lockKey))){
stringRedisTemplate.delete(lockKey)
}
这里释放锁逻辑会出现并发问题。当当前线程发现这把锁属于自己,并准备删除锁的时候。刚好这个时候锁超时了,锁自动释放,其他线程这个时候拿到了锁。
当前线程觉得这把锁是属于自己的,所以理所应当的把锁释放了。这时也相当于A线程把B线程的锁释放了。这个问题出现的核心是由于锁过期时间,到期自动释放导致。
解决办法: 单纯延长过期时间治标不治本。
在主线程创建分布式锁的时候,创建一个子线程,定时(一定要小于锁过期时间)去延长锁的过期时间,让锁在主线程不退出的情况下,永远不过期。当主线程退出后,子线程也相应退出。