Redis分布式锁原理解析

  • 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("********************************************");
    }

点击进入GitHub 查看代码

  • 5
    点赞
  • 47
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值