基于Redis实现分布式锁

1.前言

目前系统架构是分布式,因为场景要保证数据一致性,且项目采用的是redis作为缓存,故采用reids实现分布式锁

2.实现方案

采用 redis中的 setNx + expire 实现分布式锁

setNx(key, value): 如果key存在,则返回0. 否则返回1

expire: 过期时间

3.代码实现(Demo)

	/**
	 * @Description 是否获取到锁
	 * @param:
	 * @param jedis
	 * @param lockKey  键
	 * @param value    值
	 * @param expireTime 过期时间
	 * @return boolean
	 * @author Cc
	 * @date 2020/2/6 11:26
	 */
	public static boolean lock(Jedis jedis, String lockKey, String value, int expireTime) {
		//key存在的情况下,不操作redis内存,也就是返回值是0。否则返回1
		Long result = jedis.setnx(lockKey, value);
		//设置锁
		if (result == 1) {
			//获取锁成功
			//若在这里程序突然崩溃,则无法设置过期时间,将发生死锁
			//通过过期时间删除锁
			jedis.expire(lockKey, expireTime);
			return true;
		}
		return false;
	}

分析

  • 因为 setnx + expire 不是原子操作,线程不安全
  • 如果程序设置锁之后,因为程序突然崩溃,则无法设置过期时间,将发送死锁。故有以下2种解决方案

4.解决死锁

4.1 采用 lua 脚本

// 加锁脚本,KEYS[1] 要加锁的key,ARGV[1]是UUID随机值,ARGV[2]是过期时间
private static final String SCRIPT_LOCK = "if redis.call('setnx', KEYS[1], ARGV[1]) == 1 then redis.call('pexpire', KEYS[1], ARGV[2]) return 1 else return 0 end";

// 解锁脚本,KEYS[1]要解锁的key,ARGV[1]是UUID随机值
private static final String SCRIPT_UNLOCK = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";

4.2 采用set命令

要求redis版本 2.6 版本之后才可以用这个命令

public static boolean lock(Jedis jedis, String lockKey, String value, int expireTime) {
		String result = jedis.set(lockKey, value, "NX", "PX", expireTime);
		if ("OK".equals(result)) {
			return true;
		}
		return false;
	}

产生的问题:
虽然 SETNX()方式能够保证设置锁和过期时间的原子性,但是如果设置的过期时间比较短,而执行业务时间比较长,就会存在锁代码块失效的问题,导致其他客户端也能获取到同样的锁,执行同样的业务,此时可能会出现一些问题。
解决方案建议:
可以将key的过期时间设置长一点,上诉问题就不出出现,但是设置多长时间,具体还需要根据业务场景权衡

书里面学到的,总结以下,旨在提供思路。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值