为什么要使用分布式锁
大多数业务服务中,都是以集群的形式部署。高并发场景下对服务共享变量进行量的操作,很容易出现并发问题。 因此需要一个分布式锁来对共享变量进行控制。
Redis 实现分布式锁思路
为什么使用Redis实现?
Redis 内部指令是单线程执行,可以利用其原子性实现锁的目的。
实现思路
最简单的思路: 获取锁,执行业务,删除锁
if setnx(k,v)
do sth
else
do noth
finally
del k
问题: 锁的释放需要外部业务程序管理,若出现异常,会造成死锁
优化思路2: 获取锁,设置有效期(两个命令合二为一)
//在最初的版本中,setnx 和 expire是两个指令,因此不具有原子性.
//redis高版本增强了set的功能,可以通过set实现这两个指令的功能.
if ( set(key,v,EX,seconds,NX) != ok)
return
else{
do sth
del key
}
问题: 锁的释放,如果主动释放需要控制 :解铃还须系铃人
代码实现
//加锁
/**
* 尝试获取分布式锁
* @param jedis Redis客户端
* @param lockKey 锁
* @param requestId 当前线程的唯一标识
* @param expireTime 超期时间
* @return 是否获取成功
*/
public static boolean tryGetDistributedLock(Jedis jedis, String lockKey, String requestId, long expireTime) {
String result = jedis.set(lockKey, requestId, NX, EX, expireTime);
if ("ok".equals(result)) {
return true;
}
return false;
}
/**
* 释放分布式锁
* @param jedis Redis客户端
* @param lockKey 锁
* @param requestId 线程标识
* @return 是否释放成功
*/
public static boolean releaseDistributedLock(Jedis jedis, 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 (1.equals(result)) {
return true;
}
return false;
}
这个解锁方案相对来锁保险一点,但是如果超时了 业务逻辑还没执行完,还是会出现并发问题。
,也可以考虑使用重入锁进行解锁;
解锁方案其实可以和业务逻辑绑定到一起,这样可以直接del,避免多个客户端解锁。