Redis锁竟然毫无作用(redis锁没有用)
Redis是一个个开源的内存数据库,广受应用程序开发者的欢迎,他可以替代关系型数据库在内存中操作数据,操作速度极快。通常情况下,我们可以使用Redis的分布式锁来有效解决共享资源抢占问题,但是在实际项目中,有时Redis锁会毫无作用!下文将分析出现这种情况的原因,并提供相关的解决办法。
Redis锁由Redis字符串类型,也就是SETNX实现。STENX有三个基本操作:当且仅当给定的key不存在时,将key的值设置为value,并返回true;否则,什么也不做,并返回false。如此一来,当某一节点执行了SETNX后,如果反到true,那么该节点即为获得锁的节点,执行对应的操作;而其它节点如果判断反结果为false,那么就无法执行相应的操作,实现了锁的作用。
那么,redis锁毫无作用的原因在于SETNX类型的不可靠性,一个经典的场景是:网络抖动导致,多台机器同一个时刻发起SETNX请求,这样,此时可能有一部分请求返结果为true,一部分返结果为false,此时,获取到锁的机器可以去执行操作,而另一部分判断为false的机器就无法执行。这样一来,最后造成的就是多台机器同时执行了操作,最终导致Redis锁竟然毫无作用!
接下来,就是解决的办法!业务最好在同一台机器上进行锁定操作,比如项目采用Spring Boot生成,应用容器就可以负责同一段逻辑作为一个单元,其他节点是不会继续发布该请求的;做到细针密缝,采用锁的重入机制,比如当没有持有锁的时候可以重新申请上锁,而在任务完成以后释放锁,加上重试机制可以有效避免setnx的不可靠性。
public boolean getLock(String locKey, long expireTime) {
boolean retFlag = false;
//重试获取锁
while (!retFlag) {
boolean lockOK = redisTemplate.opsForValue().setIfAbsent(locKey, expireTime);
if(lockOK) { //锁申请成功,返回true
retFlag = true;
break;
}
//锁申请不成功,判断锁是否超时,超时则继续获取锁
Long lockTimeOut = (Long)redisTemplate.opsForValue().get(locKey);
if(System.currentTimeMillis() > lockTimeOut) {
redisTemplate.delete(expireTime);
}
}
return retFlag;
}
综上所述,实际上redis锁的失效分为两种情况:
一是SETNX的不可靠性;二是业务逻辑的失误,比如没有释放锁等。因此,在编写代码锁的时候,一定要非常谨慎,保证业务正确性,避免出现redis锁竟然毫无作用的上述情况!
相关文章