大家好呀,我是楼仔 。
今天第一天开工,收拾心情,又要开始好好学习,好好工作了 。
对于使用 JAVA 的小伙伴,其实我们完全不用手动撸一个分布式锁,直接使用 redisson 就行 。
但是因为这些封装好的组建,让我们越来越懒 。
我们使用一些封装好的开源组建时,可以了解其中的原理,或者自己动手写一个,可以更好提升你的技术水平 。
今天我就教大家用原生的 Redis , 手动撸一个 Redis 分布式锁,很有意思 。
01 问题引入其实通过 Redis 实现分布式锁 , 经常会有面试官会问 , 很多同学都知道用 SetNx() 去获取锁,解决并发问题 。
SetNx() 是什么?我简单解答一下 。
Redis Setnx(SET if Not eXists) 命令在指定的 key 不存在时,为 key 设置指定的值 。
对于下面 2 种问题,你知道如何解决么?
- 如果获取锁的机器挂掉 , 如何处理?
- 当锁超时时 , A、B 两个线程同时获取锁,可能导致锁被同时获取,如何解决?
02 理论知识刚才说过 , 通过 SetNx() 去获取锁,可以解决并发问题 。
当获取到锁 , 处理完业务逻辑后,会将锁释放 。
文章插图
图片
但当机器宕机,或者重启时 , 没有执行 Del() 删除锁操作 , 会导致锁一直没有释放 。
所以,我们还需要记录锁的超时时间,判断锁是否超时 。
文章插图
图片
这里我们通过 GetKey() 获取锁的超时时间 A,通过和当前时间比较,判断锁是否超时 。
如果锁未超时,直接返回,如果锁超时,重新设置锁的超时时间 , 成功获取锁 。
还有其它问题么?当然!
因为在并发场景下,会存在 A、B 两个线程同时执行 SetNx(),导致两个线程同时获取到锁 。
那如何解决呢?将 SetNx() 用 GetSet() 替换 。
文章插图
图片
GetSet() 是什么?我简单解答一下 。
Redis Getset 命令用于设置指定 key 的值,并返回 key 的旧值 。
这里不太好理解,我举个例子 。
假如 A、B 两个线程,A 先执行,B 后执行:
- 对于线程 A 和 B,通过 GetKey 获取的超时时间都是 T1 = 100;
- 对于线程 A , 将超时时间 Ta = 200 通过 GetSet() 设置,返回 T2 = 100,此时满足条件 “T1 == T2”,获取锁成功;
- 对于线程 B,将超时时间 Tb = 201 通过 GetSet() 设置,由于锁超时时间已经被 A 重新设置,所以返回 T2 = 200 , 此时不满足条件 “T1 == T2”,获取锁失败 。
其实在现实并发场景中,能走到这一步,基本是“同时”进来的,两者的时间差非常小,可以忽略此影响 。
03 代码实战这里给出 Go 代码,注释都写得非常详细,即使你不会 Go,读注释也能读懂 。
// 获取分布式锁,需要考虑以下情况:// 1. 机器A获取到锁,但是在未释放锁之前 , 机器挂掉或者重启,会导致其它机器全部hang住,这时需要根据锁的超时时间,判断该锁是否需要重置;// 2. 当锁超时时,需要考虑两台机器同时去获取该锁,需要通过GETSET方法 , 让先执行该方法的机器获取锁,另外一台继续等待 。func GetDistributeLock(key string, expireTime int64) bool { currentTime := time.Now().Unix() expires := currentTime + expireTime redisAlias := "jointly" // 1.获取锁,并将value值设置为锁的超时时间 redisRet, err := redis.SetNx(redisAlias, key, expires) if nil == err && utils.MustInt64(1) == redisRet {// 成功获取到锁return true } // 2.当获取到锁的机器突然重启&挂掉时,就需要判断锁的超时时间,如果锁超时,新的机器可以重新获取锁 // 2.1 获取锁的超时时间 currentLockTime, err := redis.GetKey(redisAlias, key) if err != nil {return false } // 2.2 当"锁的超时时间"大于等于"当前时间",证明锁未超时,直接返回 if utils.MustInt64(currentLockTime) >= currentTime {return false } // 2.3 将最新的超时时间 , 更新到锁的value值 , 并返回旧的锁的超时时间 oldLockTime, err := redis.GetSet(redisAlias, key, expires) if err != nil {return false } // 2.4 当锁的两个"旧的超时时间"相等时,证明之前没有其它机器进行GetSet操作,成功获取锁 // 说明:这里存在并发情况 , 如果有A和B同时竞争,A会先GetSet,当B再去GetSet时,oldLockTime就等于A设置的超时时间 if utils.MustString(oldLockTime) == currentLockTime {return true } return false}
推荐阅读
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 一个人经常“间歇性回你微信”,说明了什么
- 微信小技巧,教你如何单独隐藏一个人的聊天记录!看完涨知识了
- 陕西风味美食脆嫩鲜香清爽
- 两个“百亿导演”输给一个80后女演员,中国电影已告别大导演时代
- 怎么忘记一个人 怎么忘记一个人手机号码
- 小舅子来治病,在我家住了一个月,住在同城的他儿子觉得理所应当
- 抖音账号不用了怎么注销,抖音有一个号登不上去了怎么注销
- 泰国旅游一个人需要花多少钱 泰国自由行一个人大概需要多少钱
- 2024春晚“最拉胯”三大节目出炉,一个比一个尬,排名你认可吗?
- 玻璃鱼缸漏水怎么处理,用玻璃胶粘了一个鱼缸要漏水要怎么拆掉