Redis分布式锁常见问题
2022-10-24 17:34:43 17 举报
AI智能生成
Redis分布式锁常见问题
作者其他创作
大纲/内容
非原子操作
setNx命令
if (jedis.setnx(lockKey, val) == 1) {
jedis.expire(lockKey, timeout);
}
jedis.expire(lockKey, timeout);
}
假如加锁成功,但是设置超时时间失败了,该lockKey就变成永不失效。
假如在高并发场景中,有大量的lockKey加锁成功了,但不会失效,有可能直接导致redis内存空间不足。
假如在高并发场景中,有大量的lockKey加锁成功了,但不会失效,有可能直接导致redis内存空间不足。
忘了释放锁
set命令
String result = jedis.set(lockKey, requestId, "NX", "PX", expireTime);
if ("OK".equals(result)) {
return true;
}
return false;
if ("OK".equals(result)) {
return true;
}
return false;
lockKey:锁的标识
requestId:请求id
NX:只在键不存在时,才对键进行设置操作。
PX:设置键的过期时间为 millisecond 毫秒。
expireTime:过期时间
set命令是原子操作,加锁和设置超时时间,一个命令就能轻松搞定。
requestId:请求id
NX:只在键不存在时,才对键进行设置操作。
PX:设置键的过期时间为 millisecond 毫秒。
expireTime:过期时间
set命令是原子操作,加锁和设置超时时间,一个命令就能轻松搞定。
超时时间才释放锁,不合理
合理的用法
手动加锁
业务操作
手动释放锁
如果手动释放锁失败了,则达到超时时间,redis会自动释放锁。
业务操作
手动释放锁
如果手动释放锁失败了,则达到超时时间,redis会自动释放锁。
try{
String result = jedis.set(lockKey, requestId, "NX", "PX", expireTime);
if ("OK".equals(result)) {
return true;
}
return false;
} finally {
unlock(lockKey);
}
String result = jedis.set(lockKey, requestId, "NX", "PX", expireTime);
if ("OK".equals(result)) {
return true;
}
return false;
} finally {
unlock(lockKey);
}
释放了别人的锁
假如线程A和线程B,都使用lockKey加锁。线程A加锁成功了,但是由于业务功能耗时时间很长,超过了设置的超时时间。这时候,redis会自动释放lockKey锁。此时,线程B就能给lockKey加锁成功了,接下来执行它的业务操作。恰好这个时候,线程A执行完了业务功能,接下来,在finally方法中释放了锁lockKey。这不就出问题了,线程B的锁,被线程A释放了
多设置了一个参数:requestId
大量失败请求
比如500毫秒内,自旋不断尝试加锁(说白了,就是在死循环中,不断尝试加锁),如果成功则直接返回。如果失败,则休眠50毫秒,再发起新一轮的尝试。如果到了超时时间,还未加锁成功,则直接返回失败。
锁重入问题
redis分布式锁是互斥的。假如我们对某个key加锁了,如果该key对应的锁还没失效,再用相同key去加锁,大概率会失败。
特殊场景
在递归方法中递归遍历多次,每次都是加的同一把锁。递归第一层当然是可以加锁成功的,但递归第二层、第三层...第N层,不就会加锁失败了?
因为从根节点开始,第一层递归加锁成功,还没释放锁,就直接进入第二层递归。因为锁名为lockKey,并且值为requestId的锁已经存在,所以第二层递归大概率会加锁失败,然后返回到第一层。第一层接下来正常释放锁,然后整个递归方法直接返回了。
redisson可重入锁
锁竞争问题
写入的操作比较少,反而有大量读取的操作。这样直接使用普通的redis分布式锁,有点浪费性能
锁的粒度越粗,多个线程抢锁时竞争就越激烈,造成多个线程锁等待的时间也就越长,性能也就越差。
读写锁
没有必要给读操作加互斥锁,只要保证读写、写写并发操作上锁是互斥的就行
读与读是共享的,不互斥
读与写互斥
写与写互斥
读与写互斥
写与写互斥
redisson.getReadWriteLock
锁分段
锁超时问题
TimerTask类,来实现自动续期
redisson
主从复制的问题
如果redis存在多个实例。比如:做了主从,或者使用了哨兵模式,基于redis的分布式锁的功能
redis加锁操作,都在master上进行,加锁成功后,再异步同步给所有的slave。
master节点由于某些不可逆的原因,挂掉了
这样需要找一个slave升级为新的master节点,假如slave1被选举出来了
如果有个锁A比较悲催,刚加锁成功master就挂了,还没来得及同步到slave1
这样会导致新master节点中的锁A丢失了。后面,如果有新的线程,使用锁A加锁,依然可以成功,分布式锁失效了。
这样需要找一个slave升级为新的master节点,假如slave1被选举出来了
如果有个锁A比较悲催,刚加锁成功master就挂了,还没来得及同步到slave1
这样会导致新master节点中的锁A丢失了。后面,如果有新的线程,使用锁A加锁,依然可以成功,分布式锁失效了。
redisson框架为了解决这个问题,提供了一个专门的类:RedissonRedLock,使用了Redlock算法。
0 条评论
下一页