文章插图
打开 Redis Desktop Manager 工具,看下到底它存的是什么 。原来在加锁的时候,写入了一个 HASH 类型的值,key 是锁名称 jackxu,field 是线程的名称,而 value 是 1(即表示锁的重入次数) 。
文章插图
小伙伴可能觉得我在一派胡言,没关系,我们点进去看下它的源码是具体实现的 。
文章插图
点进 tryLock() 方法的 tryAcquire() 方法,再到->tryAcquireAsync() 再到->tryLockInnerAsync(),终于见到庐山真面目了,原来它最终也是通过 Lua 脚本来实现的 。
文章插图
现在我把这段Lua脚本拿出来分析一下,很简单 。
// KEYS[1] 锁名称 updateAccount// ARGV[1] key 过期时间 10000ms// ARGV[2] 线程名称// 锁名称不存在if (redis.call('exists', KEYS[1]) == 0) then// 创建一个 hash,key=锁名称,field=线程名,value=https://www.isolves.com/it/cxkf/bk/2020-07-29/1redis.call('hset', KEYS[1], ARGV[2], 1);// 设置 hash 的过期时间redis.call('pexpire', KEYS[1], ARGV[1]);return nil;end;// 锁名称存在,判断是否当前线程持有的锁if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) then// 如果是,value+1,代表重入次数+1redis.call('hincrby', KEYS[1], ARGV[2], 1);// 重新获得锁,需要重新设置 Key 的过期时间redis.call('pexpire', KEYS[1], ARGV[1]);return nil;end;// 锁存在,但是不是当前线程持有,返回过期时间(毫秒)return redis.call('pttl', KEYS[1]);
unlock() 中的 unlockInnerAsync() 释放锁,同样也是通过 Lua 脚本实现 。// KEYS[1] 锁的名称 updateAccount// KEYS[2] 频道名称 redisson_lock__channel:{updateAccount}// ARGV[1] 释放锁的消息 0// ARGV[2] 锁释放时间 10000// ARGV[3] 线程名称// 锁不存在(过期或者已经释放了)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) thenreturn nil;end;// 锁存在,是当前线程加的锁// 重入次数-1local counter = redis.call('hincrby', KEYS[1], ARGV[3], -1);// -1 后大于 0,说明这个线程持有这把锁还有其他的任务需要执行if (counter > 0) then// 重新设置锁的过期时间redis.call('pexpire', KEYS[1], ARGV[2]);return 0;else// -1 之后等于 0,现在可以删除锁了redis.call('del', KEYS[1]);// 删除之后发布释放锁的消息redis.call('publish', KEYS[2], ARGV[1]);return 1;end;// 其他情况返回 nilreturn nil;
看完它的使用后,我们发现真的使用起来像 JDK 中的 ReentrantLock 一样丝滑 。文章插图
RedLockRedLock 的中文是直译过来的,就叫红锁 。红锁并非是一个工具,而是 Redis 官方提出的一种分布式锁的算法 。我们知道如果采用单机部署模式,会存在单点问题,只要 redis 故障了,加锁就不行了 。如果采用 master-slave 模式,加锁的时候只对一个节点加锁,即便通过 sentinel 做了高可用,但是如果 master 节点故障了,发生主从切换,此时就会有可能出现锁丢失的问题 。基于以上的考虑,其实 redis 的作者 Antirez 也考虑到这个问题,他提出了一个 RedLock 的算法 。
我在这里画了一个图,图中这五个实例都是独自部署的,没有主从关系,它们就是5个 master 节点 。
文章插图
通过以下步骤获取一把锁:
- 获取当前时间戳,单位是毫秒
- 轮流尝试在每个 master 节点上创建锁,过期时间设置较短,一般就几十毫秒
- 尝试在大多数节点上建立一个锁,比如5个节点就要求是3个节点(n / 2 +1)
- 客户端计算建立好锁的时间,如果建立锁的时间小于超时时间,就算建立成功了
- 要是锁建立失败了,那么就依次删除这个锁
- 只要别人建立了一把分布式锁,你就得不断轮询去尝试获取锁
最后 Redisson 官网上也给出了如何使用红锁 redlock,几行代码搞定,依然很丝滑,感兴趣的小伙伴可以看下 。
推荐阅读
- 电脑的辐射
- 怎么消除紧张
- 常见的花茶价格介绍,勿忘我花茶的价格勿忘我花茶的泡法
- 女人不管出门多赶,尽量化淡妆!记住这3个窍门,简单易学显气色
- 不要随意触摸车内这四个按钮,否则会有车辆损坏和人员死亡的危险
- 常见草莓的区别和吃法
- 为什么语音里自己的声音能这么难听?
- 家常炒拉面有技巧,这样做比面馆都好吃还不油腻,食材多,真营养
- 修水龙头是什么梗?
- 年底想体检,却不知道查什么?这份千元以下的体检清单请收好