- Redis 分布式锁命令
setnx当且仅当 key 不存在。若给定的 key 已经存在,则setnx不做任何动作。setnx是『set if not exists』(如果不存在,则 set)的简写,setnx具有原子性。
getset先 get 旧值,后set 新值,并返回 key 的旧值(old value),具有原子性。当 key 存在但不是字符串类型时,返回一个错误;当key 不存在的时候,返回nil ,在Java里就是 null。
expire设置 key 的有效期
del删除 key - 与时间戳的结合
分布式锁的值是按 系统当前时间System.currentTimeMillis()+Key 有效期组成 - Redis 分布式锁流程图

- Redis 分布式锁优化版流程

这个优化版的流程这里说明一下,如果获取锁成功,那么按照流程执行业务逻辑,执行完毕,删除锁。如果没有获取到锁,继续判断时间戳,看是否可以重置并获取到锁,先 get 得到当前的值,并且比较一下当前的系统时间和得到的值得大小,如果当前时间大于值,说明锁已经过期了,接下来就执行 getset 操作,将该key的值重新设置一下,如果返回的旧值不存在,说明这个key 已经被删除了,或者这个旧值存在,并且和之前get 的值相同,说明在重置锁的过程中,锁没有发生变化,此时可以获取到锁了,接下来执行相应的业务逻辑。
// 例如这段关闭订单的代码
@Scheduled(cron = "0 */1 * * * ?")// 每 1 分钟执行一次
public void closeOrderTask() {
log.info("关闭订单定时任务启动");
long lockTimeOut = Long.parseLong(PropertiesUtil.getProperty("lock.timeout","5000"));
Long setnxResult = RedisShardedPoolUtil.setnx(Const.REDIS_LOCK.CLOSE_ORDER_TASK_LOCK, String.valueOf(System.currentTimeMillis() + lockTimeOut));
if (setnxResult != null && setnxResult.intValue() == 1) {
closeOrder(Const.REDIS_LOCK.CLOSE_ORDER_TASK_LOCK);
} else {
// 未获取到锁,继续判断时间戳,看是否可以重置并获取到锁
String lockValueA = RedisShardedPoolUtil.get(Const.REDIS_LOCK.CLOSE_ORDER_TASK_LOCK);
if (lockValueA != null && System.currentTimeMillis() > Long.parseLong(lockValueA)) {
String lockValueB = RedisShardedPoolUtil.getSet(Const.REDIS_LOCK.CLOSE_ORDER_TASK_LOCK, String.valueOf(System.currentTimeMillis() + lockTimeOut));
//再次用当前时间戳getSet
//返回给定的 key 旧值, > 旧值判断,是否可以获取锁
//当 key 没有旧值得时候, 即key 不存在的时候,返回 nil -> 获取锁
//这里我们set了一个新的value 值,获取旧的值
if (lockValueB == null || (lockValueB != null && StringUtils.equals(lockValueA, lockValueB))) {
//真正获取到锁
closeOrder(Const.REDIS_LOCK.CLOSE_ORDER_TASK_LOCK);
} else {
log.info("没有获取到分布式锁:{}", Const.REDIS_LOCK.CLOSE_ORDER_TASK_LOCK);
}
} else {
log.info("没有获取到分布式锁:{}", Const.REDIS_LOCK.CLOSE_ORDER_TASK_LOCK);
}
}
log.info("关闭订单定时任务结束");
}
private void closeOrder(String lockName) {
RedisShardedPoolUtil.expire(lockName, 5);// 设置有效期 50 秒 防止死锁, 线上生产环境应该设置为 5 秒
log.info("获取{},ThreadName:{}", Const.REDIS_LOCK.CLOSE_ORDER_TASK_LOCK, Thread.currentThread().getName());
int hour = Integer.parseInt(PropertiesUtil.getProperty("close.order.task.time.out", "2"));
iOrderService.closeOrder(hour);
RedisShardedPoolUtil.del(Const.REDIS_LOCK.CLOSE_ORDER_TASK_LOCK);
log.info("释放{},ThreadName:{}", Const.REDIS_LOCK.CLOSE_ORDER_TASK_LOCK, Thread.currentThread().getName());
log.info("********************************************");
}
版权声明:本文为Andy86869原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。