redis的分布式锁

1.分布式锁

  • 单应用中使用锁:(单进程多线程)
synchronize、ReentrantLock
  • 分布式应用中使用锁:(多进程多线程)
分布式锁是控制分布式系统之间同步访问共享资源的一种方式

2.分布式锁的实现方式

  • 基于数据库的乐观锁实现分布式锁
  • 基于zookeeper临时节点的分布式锁
  • 基于Redis的分布式锁

3.分布式锁的注意事项

  • 互斥性:在任意时刻,只有一个客户端能持有锁
  • 同一性:加锁和解锁必须是同一个客户端,客户端自己不能把别人加的锁给解了。
  • 可重入性:即使有一个客户端在持有锁的期间崩溃而没有主动解锁,也能保证后续其他客户端能加锁。

3.redis分布式锁解决的问题

保证setnx()和expire的 原子性,从而保证redis的原子性。 wiki上解释说:所谓原子性,就是执行过程中不会被其他的线程所影响。也就是说任务一旦开始执行,执行过程中不会出现cpu的切换,直到任务执行结束。

setnx()可以保证一个锁只被一个客户端持有,使用完成,再用del释放。但是如果setnx()执行完后,
出现异常没有调用del,就会导致锁无法释放的问题。所以可以添加一个expire超时时间,
setnx()后即使出现异常,超时后,也可以自动的释放锁。但是setnx和expire不是原子操作,
如果这两个一起执行就不会出现问题了,因为expire依赖setnx的执行结果,如果setnx没有抢到锁,
不能执行expire操作。

4.解决方案

方案一: redis2.8中加入了set的扩展参数,使setnx和expire可以一起执行,解决了这个问题:

/**
	 * 使用redis的set命令实现获取分布式锁
	 * @param lockKey   	可以就是锁
	 * @param requestId		请求ID,保证同一性
	 * @param expireTime	过期时间,避免死锁
	 * @return
	 */
	public static boolean getLock(String lockKey,String requestId,int expireTime) {
		//NX:保证互斥性
		String result = jedis.set(lockKey, requestId, "NX", "EX", expireTime);
		if("OK".equals(result)) {
			return true;
		}
		
		return false;
	}

方案二:redis+lua脚本实现(推荐)

public static boolean releaseLock(String lockKey, String requestId) {
		String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
		Object result = jedis.eval(script, Collections.singletonList(lockKey), Collections.singletonList(requestId));

		if (result.equals(1L)) {
			return true;
		}
		return false;
	}

5.不可用场景

超时问题:如果redis加了锁后,在加锁和释放锁之间的逻辑处理太长,以至于超出了锁的超时时间,是得第一次操作还没有完成,第二次操作就获取到了锁,不能保证第一次操作的原子性。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值