目录
edisson分布式锁原理剖析
相信使用过redis的,或者正在做分布式开发的童鞋都知道redisson组件,它的功能很多,但我们使用最频繁的应该还是它的分布式锁功能,少量的代码,却实现了加锁、锁续命(看门狗)、锁订阅、解锁、锁等待(自旋)等功能,我们来看看都是如何实现的。
加锁
//获取锁对象
RLock redissonLock = redisson.getLock(lockKey);
//加分布式锁
redissonLock.lock();
根据redissonLock.lock()
方法跟踪到具体的private <T> RFuture<Long> tryAcquireAsync(long leaseTime, TimeUnit unit, final long threadId)
方法,真正获取加锁的逻辑是在tryAcquireAsync
该方法中调用的tryLockInnerAsync()
方法,看看这个方法是怎么实现的?
<T> RFuture<T> tryLockInnerAsync(long leaseTime, TimeUnit unit, long threadId, RedisStrictCommand<T> command) {
internalLockLeaseTime = unit.toMillis(leaseTime);
return commandExecutor.evalWriteAsync(getName(), LongCodec.INSTANCE, command,
// 判断是否存在分布式锁,getName()也就是KEYS[1],也就是锁key名
"if (redis.call('exists', KEYS[1]) == 0) then " +
// 加锁,执行hset 锁key名 1
"redis.call('hset', KEYS[1], ARGV[2], 1); " +
// 设置过期时间
"redis.call('pexpire', KEYS[1], ARGV[1]); " +
"return nil; " +
"end; " +
// 这个分支是redisson的重入锁逻辑,锁还在,锁计数+1,重新设置过期时长
"if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) then " +
"redis.call('hincrby', KEYS[1], ARGV[2], 1); " +
"redis.call('pexpire', KEYS[1], ARGV[1]); " +
"return nil; " +
"end; " +
// 返回锁的剩余过期时长
"return redis.call('pttl', KEYS[1]);",
Collections.<Object>singletonList(getName()), internalLockLeaseTime, getLockName(threadId));
}
发现底层是结合lua脚本实现了加锁逻辑。
为什么底层结合了Lua脚本?
Redis是在2.6推出了脚本功能,允许开发者使用Lua语言编写脚本传到redis执行。使用脚本的好处如下:
1、减少网络开销:本来5次网络请求的操作,可以用一个请求完成,原先5次请求的逻辑,可以一次性放到redis中执行,较少了网络往返时延。这点跟管道有点类似。