redisson 分布式锁lua脚本解析

您所在的位置:网站首页 redisson可重入锁的实现 redisson 分布式锁lua脚本解析

redisson 分布式锁lua脚本解析

2023-05-11 14:54| 来源: 网络整理| 查看: 265

简介

由于redis是单线程的,所以看lua脚本的时候只需要使用单线程的思维去看就行了,而且个人不建议使用lua脚本编写太复杂的业务,特别是有循环的,写得不好可能会使redis陷入卡顿,甚至死循环直接卡死无法运行其他命令。感兴趣可以自己试一下。

问题1 :为什么 redisson 实现分布式锁的数据类型是 hash 而不是 string。

我的理解:为了支持两个参数 ( 可重入计数 + 线程标识 ) ,如果不使用hash无法实现。其中这个hash里只有一个元素,不会出现两个元素。

非公平锁

实现方式 :hash + PubSub

加锁(tryLockInnerAsync) --[[ 参数 Collections.singletonList( this.getName() -- key1 ), new Object[]{ this.internalLockLeaseTime, --arg1 this.getLockName(threadId)} --arg2 ]]-- -- 如果锁对应的hash不存在 if (redis.call('exists', KEYS[1]) == 0) then -- 加锁成功,并且设置过期时间 redis.call('hset', KEYS[1], ARGV[2], 1); redis.call('pexpire', KEYS[1], ARGV[1]); return nil; end; -- 如果锁存在 if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) then -- 进行计数+1 (为了可重入) redis.call('hincrby', KEYS[1], ARGV[2], 1); redis.call('pexpire', KEYS[1], ARGV[1]); return nil; end; -- 获取剩余过期时间 return redis.call('pttl', KEYS[1]); 解锁(unlockInnerAsync) --[[ 参数 Arrays.asList( this.getName(), --key1 this.getChannelName() --key2 ), new Object[]{ LockPubSub.unlockMessage, --arg1 this.internalLockLeaseTime, --arg2 this.getLockName(threadId) --arg3 } ]]-- ​ -- 锁对应的hash不存在 if (redis.call('exists', KEYS[1]) == 0) then -- 通知抢锁。 redis.call('publish', KEYS[2], ARGV[1]); --结束 return 1; end; -- 如果锁不存在,不处理 if (redis.call('hexists', KEYS[1], ARGV[3]) == 0) then return nil; end; --对其中的元素进行计数-1 实现可重入 local counter = redis.call('hincrby', KEYS[1], ARGV[3], -1); -- 如果此时还有计数 if (counter > 0) then -- 刷新过期时间 redis.call('pexpire', KEYS[1], ARGV[2]); return 0; else -- 解锁,通知其他线程争抢锁。 redis.call('del', KEYS[1]); redis.call('publish', KEYS[2], ARGV[1]); return 1; end; return nil;

 

公平锁

实现方式 :list + zset + hash + PubSub

zset用于处理过期时间

list用于处理先后顺序

加锁 (tryLockInnerAsync) --[[ 参数 Arrays.asList( this.getName(), --key1 this.threadsQueueName, --key2 this.timeoutSetName), --key3 new Object[]{ this.internalLockLeaseTime, --arg1 this.getLockName(threadId), --arg2 currentTime + 5000L, --arg3 currentTime} --arg4 ]]-- while true do -- 取出队列中队头元素 local firstThreadId2 = redis.call('lindex', KEYS[2], 0); --队列中没有元素了就结束 if firstThreadId2 == false then break; end; ​ -- 从zset中获取对应元素的过期时间 local timeout = tonumber(redis.call('zscore', KEYS[3], firstThreadId2)); -- 已到过期时间 if timeout 0) then redis.call('pexpire', KEYS[1], ARGV[2]); --结束 return 0; end; -- 解锁 redis.call('del', KEYS[1]); -- 获取队列的队头元素 local nextThreadId = redis.call('lindex', KEYS[2], 0); -- 如果还有元素,通知下一个线程抢锁。 if nextThreadId ~= false then redis.call('publish', KEYS[4] .. ':' .. nextThreadId, ARGV[1]); end; --结束 return 1;

 



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3