前言:
研究加锁的初衷是:监听redis过期消息提醒,同一个数据(键)过期会有多次通知提醒。原因是:可能是由于 Redis 的主从复制或者分片集群等机制导致的。在主从复制或者分片集群中,可能会发生多个节点同时订阅了相同的键空间通知,从而导致同一个键空间事件被多次触发。
我的解决方法是:给键过期后提醒的回调函数加锁,收到多个通知提醒,回调函数加锁后最终只会有一个执行,其他没有获得锁的回调不会执行,这样就避免了重复执行任务代码。
一、使用redis命令
(1) 在加锁时就要给锁设置一个标识,进程要记住这个标识。当进程解锁的时候,要进行判断,是自己持有的锁才能释放,否则不能释放。可以为key 赋一个随机值,来充当进程的标识。
(2)解锁时要先判断、再释放,这两步需要保证原子性,否则第二步失败的话,就会出现死锁。而获取和删除命令不是原子的,这就需要采用Lua 脚本,通过 Lua 脚本将两个命令编排在一起,而整个Lua脚本的执行过程是原子的。
按照以上思路最终方案如下:
加锁
set key random-value nx ex seconds
解锁
if redis.call("get",KEYS[1]) == ARGV[1] then
return redis.call("del",KEYS[1])
else
return 0 end
二、使用RedisTemplate实现
封装出自己的RedisLock类
/**
* Redis 分布式锁
*
**/
@Component
public class RedisLockUtils {
@Autowired
private RedisTemplate redisTemplate;
//分布式锁过期时间 s 可以根据业务自己调节
private static final Long LOCK_REDIS_TIMEOUT = 10L;
//分布式锁休眠 至 再次尝试获取 的等待时间 ms 可以根据业务自己调节
public static final Long LOCK_REDIS_WAIT = 500L;
/**
* 加锁
**/
public Boolean getLock(String key,String value){
<