Redis实战-Redisson-分布式锁
1. 简介
随着技术的快速发展,业务系统规模的不断扩大,分布式系统越来越普及。一个应用往往会部署到多台机器上,在一些业务场景中,为了保证数据的一致性,要求在同一时刻,同一任务只在一个节点上运行,保证同一个方法同一时刻只能被一个线程执行。这时候分布式锁就运用而生了。
分布式锁有很多的解决方案。常见的有:
-
基于数据库的:悲观锁,乐观锁。
-
基于zookeeper的分布式锁。
-
本章中讲的基于redis的分布式锁。
2. 超卖
下单减库存是互联网项目中必不可少的环节。然而,如果我么考虑不得当,将会带来很多问题。比如最不能忍受的:超卖
如下代码,一个初始化库存的方法和一个购买图书的方法,我们没有做任何的并发处理,查看下最终结果。
启动测试:
这里我们使用jemter
来进行并发请求。配置如下:
线程组配置:
请求配置:
请求结果:
只复制了部分日志
通过日志很明显的看到,即使在业务代码中判断了库存 > 0
但还是超卖了。
3. redis setnx
主要是用redis的 setnx (set not exists)命令实现分布式锁。
3.1 编写逻辑
在超买的场景中,我们了解了分布式锁的必要性。
上面的场景如果是单机的话,直接使用jvm锁就能解决问题,但是在分布式场景下下jvm锁无法处理。
接下来我们将使用redis命令来解决一下超卖问题。
-
新增了锁标识key。
-
在进行业务处理之前,给redis中
setIfAbsent(LOCK_KEY, clientId, 30, TimeUnit.SECONDS)
作为lock。LOCK_KEY
:锁的标识,比如秒杀的商品id_lock
:当对该商品进行秒杀下单时,加锁使其线性执行。clientId
:当前请求的唯一值,为了在删除锁时进行锁判断。即只能删除自己加的锁。防止误删锁。30
:失效时间,防止死锁(如果加锁时不设置过期时间,当系统执行完加锁还未进行解锁时系统宕机,那其他节点也无法进行下单,因为锁一直在)。 -
解锁逻辑最好放在
finally
中进行,防止报错导致死锁。
3.2 启动测试
启动两个服务,并配置nginx负载均衡。
nginx配置如下:
jemter配置如下:
启动测试:
redis中查看库存:
打完收工~
3.3 小节
这里主要是用了setnx
来实现分布式锁。虽然解决了超卖问题,但其中还是有很多缺陷。比如:
- 当请求获取锁失败时,能不能尝试重新获取锁或者阻塞等待获取锁,而不是直接返回
系统繁忙
之类的提示语。 - 如果持有锁的请求处理时间超过了设置的超时时间,也就是业务逻辑还没有处理完呢,但锁已经失效了,此时刚好又进来一个请求,又有并发问题了😂。
此时:redisson
:申请出战🙋。
4. redisson
4.1 简介
Redisson是一个在Redis的基础上实现的Java驻内存数据网格(In-Memory Data Grid)。它不仅提供了一系列的分布式的Java常用对象,还提供了许多分布式服务。其中包括(BitSet
, Set
, Multimap
, SortedSet
, Map
, List
, Queue
, BlockingQueue
, Deque
, BlockingDeque
, Semaphore
, Lock
, AtomicLong
, CountDownLatch
, Publish / Subscribe
, Bloom filter
, Remote service
, Spring cache
, Executor service
, Live Object service
, Scheduler service
) Redisson提供了使用Redis的最简单和最便捷的方法。Redisson的宗旨是促进使用者对Redis的关注分离(Separation of Concern),从而让使用者能够将精力更集中地放在处理业务逻辑上。
Redisson底层采用的是Netty框架。支持Redis2.8以上版本,支持Java1.6+以上版本。
Jedis 与 Redisson
-
Jedis:Jedis 只是简单的封装了 Redis 的API库,可以看作是Redis客户端,它的方法和Redis 的命令很类似,相比于Redisson 更原生一些,更灵活。
-
Redisson:Redisson 不仅封装了 redis ,还封装了对更多数据结构的支持,以及锁等功能,相比于Jedis 更加大。
4.2 quick start
4.2.1 添加依赖
springboot 基础上添加此依赖。
4.2.2 application.yaml
因为使用的是单机redis,并且使用的是自动装配的依赖,所以直接使用redis的基本配置即可。
4.2.3 controller
4.2.4 启动测试
jemter 配置如下:
启动9个线程并同一时刻进行请求。
请求lock方法日志如下:
所有请求同一时刻进入方法,并且请求阻塞每隔两秒获取到锁。
请求tryLock方法日志如下:
所有请求同一时刻进入方法,并且只有一个请求获取到了锁,其他请求直接返回结果。
请求tryLockWithBlock方法日志如下:
所有请求同一时刻进入方法,并且只有三个请求获取到了锁。
4.2.5 小节
redisson 提供了lock()
和tryLock()
,tryLock(long time, TimeUnit unit)
,tryLock(long waitTime, long leaseTime, TimeUnit unit)
方法。
lock()
:会阻塞未获取锁的请求,默认持有30s
锁,但当业务方法在30s内没有执行完时,会有看门狗(默认每隔10s)
给当前锁续时30s
。tryLock()
:尝试获取锁,获取不到则直接返回获取失败,默认持有30s
锁,但当业务方法在30s内没有执行完时,会有看门狗(默认每隔10s)
给当前锁续时30s
。tryLock(long time, TimeUnit unit)
:尝试获取锁,等待time TimeUnit
,默认持有30s
锁,但当业务方法在30s内没有执行完时,会有看门狗(默认每隔10s)
给当前锁续时30s
。tryLock(long waitTime, long leaseTime, TimeUnit unit)
:尝试获取锁,等待waitTime TimeUnit
,锁最长持有leaseTime TimeUnit
,当业务方法在leaseTime TimeUnit
时长内没有执行完时,会强制解锁。
4.3 解决超卖
经测试不会存在超卖问题。
并且避免了3.3
小节中提到的问题。
4.4 小节
方便,好用。
__EOF__

本文链接:https://www.cnblogs.com/ludangxin/p/15145779.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角【推荐】一下。您的鼓励是博主的最大动力!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 记一次.NET内存居高不下排查解决与启示