阿里云-云小站(无限量代金券发放中)
【腾讯云】云服务器、云数据库、COS、CDN、短信等热卖云产品特惠抢购

Redis通过Lua脚本实现分布式锁实践

118次阅读
没有评论

共计 1400 个字符,预计需要花费 4 分钟才能阅读完成。

分布式锁在多实例部署,分布式系统中经常会使用到,这是因为基于 jvm 的锁无法满足多实例中锁的需求,本篇将讲下 Redis 如何通过 Lua 脚本实现分布式锁,不同于网上的 redission,完全是手动实现的。

我们先来看一个无锁的情况下会导致什么问题:

这是一个普通的更新用户年龄的功能, 各层代码如下, 访问 controller 层, 一个更新, 一个查询

Redis 通过 Lua 脚本实现分布式锁实践

这是 service 层, 我们使用 contdownlatch 发令枪来模拟线程同时并发的情况, 发令枪设为 32, 即 32 个线程同时去请求修改年龄,

Redis 通过 Lua 脚本实现分布式锁实践

   这里使用线程池来提交多线程任务, 看代码知道, 这里我们已经有了判断年龄的操作, 当查询用户查询大于 0 时, 才去调更新用户年龄 - 1 的方法, 等下看看有没有用

Redis 通过 Lua 脚本实现分布式锁实践

 

这里是 sql, 可以看到两个 sql, 一个查询用户年龄, 一个会执行用户年龄每次减 1 ,

Redis 通过 Lua 脚本实现分布式锁实践

 

 这里是用户数据, 我们可以看到, 用户 UID 为 UR12324 的用户, 他的年龄是 30, 接着我们来调 32 个线程来操作减他年龄

Redis 通过 Lua 脚本实现分布式锁实践

 

 我们请求下这个方法

Redis 通过 Lua 脚本实现分布式锁实践

然后看看结果:

Redis 通过 Lua 脚本实现分布式锁实践

Redis 通过 Lua 脚本实现分布式锁实践

可以看到库中年龄已被减为 -2, 在未加锁的情况下, 查询较验并没有什么作用, 此时如果加个 synchronized 或 lock 锁肯定能避免这种情况, 但我们本文讨论的是多实例或分布式环境中, 此加锁方式仍然会产生问题, 感兴趣的可以试下是不是

 

 

 下面我们开始实现一个 redis 分布式锁, 来避免这种情况发生, 先说说实现思路:

 

 1, 线程请求访问前先调用加锁的方法, 加锁就去里生成一个随机数同时保存在线程本地变量和 redis 的某 key 中, 此 key 设有效期为 200ms, 具体值根据业务执行时间自行调整, 加锁成功;

 2. 其它线程试着访问拿出它本地变量与 redis 中某 key 进行比较, 如果不一致, 则说明有锁, 此线程休眠一段时间, 再试着加锁;

 3. 加锁成功的线程在操作结束后删掉它持有锁 (用 lua 实现, 保证原子性, 在它比对和删除锁的过程中, 其它线程不会加锁成功), 让其它线程再次加锁以执行任务;

说明: 锁的时间为 200ms 可预防线程挂掉之后死锁,200ms 后会自动释放

 

下面看看我们写的锁代码:

片段 1: 使用 redislock 实现 lock 来复写它的方法

Redis 通过 Lua 脚本实现分布式锁实践

片段 2: 试着加锁的方法

Redis 通过 Lua 脚本实现分布式锁实践

 

片段 3: 解锁方法, 此处首先从线程本地变量获取它的随机数, 然后调用 lua 脚本, 与 redis 中 key 相比较, 如果相同则删除, 否则返回 0;

Redis 通过 Lua 脚本实现分布式锁实践

  此为 lua 脚本方法, 用此方法可以保证判断和删除的原子性, 在此过程中没有线程可以操作此 key

Redis 通过 Lua 脚本实现分布式锁实践

 

到此为止, 我们锁基本写完, 来测试下有没有用:

Redis 通过 Lua 脚本实现分布式锁实践

我们在此方法前后分别加入加锁和解锁方法, 使用方式和 lock 锁一样, 我们重新把年龄恢复到 30 后来测试一下吧

先看看日志

Redis 通过 Lua 脚本实现分布式锁实践

 

这里可以看到各个线程争夺锁的情况, 再看看执行结果

Redis 通过 Lua 脚本实现分布式锁实践

Redis 通过 Lua 脚本实现分布式锁实践

 

这里我们可以看到虽然是 32 个线程并发执行, 但此值并不会变为负数, 加锁成功.

我们可以看到最后 2 个线程并没有执行方法

Redis 通过 Lua 脚本实现分布式锁实践

 

Redis 通过 Lua 脚本实现分布式锁实践

 

在具体生产环境中, 比如典型的用户余额扣减, 我们可以用用户 UID 作 KEY, 这样就不会造成 100 个用户,500 个线程争夺一个锁的情况发生,100 个用户会有 100 个锁, 此时假如每个用户 5 个请求, 一个锁只处理 5 个线程

大大提高锁的效率.

此时说明加锁成功, 大家可以在分布式环境中测试更明显, 有关极端情况下解锁失败后应该做什么也可以由我们自己决定, 比 redission 要灵活, 带锁的 redis 最好是单实例, 在集群中可能会出问题, 有机会我们再用 zk 实现下。

正文完
星哥说事-微信公众号
post-qrcode
 
星锅
版权声明:本站原创文章,由 星锅 2022-01-22发表,共计1400字。
转载说明:除特殊说明外本站文章皆由CC-4.0协议发布,转载请注明出处。
【腾讯云】推广者专属福利,新客户无门槛领取总价值高达2860元代金券,每种代金券限量500张,先到先得。
阿里云-最新活动爆款每日限量供应
评论(没有评论)
验证码
【腾讯云】云服务器、云数据库、COS、CDN、短信等云产品特惠热卖中