Redis实现 分布式缓存、分布式锁

本文探讨了Redis在分布式环境中的应用,包括如何处理堆外内存溢出、缓存穿透、缓存雪崩和缓存击穿的问题。介绍了使用本地锁和分布式锁的策略,特别强调了分布式锁的实现、死锁处理、事务原子性和锁的原子性。还提到了Redisson作为更强大的分布式锁解决方案,以及其提供的读写锁、闭锁和信号量等功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

缓存逻辑

 

d83e8c033e374f16b1248376b18b4ef6.png

代码实例

ea07458b8bd04546b99bb47470a32060.png

堆外内存溢出

 springboot2.0以后默认使用lettuce作为操作rendis的客户端,在高并发下回产生“堆外内存溢出”,可以通过切换使用jedis。

eef8654dc81146beb22f3f4010db34ec.png

缓存穿透

431768455a234e23af6afe696d80ce0f.png

缓存雪崩

6a3eb39feba244cd930a0f2d22b0fb9b.png

缓存击穿

27283067f3c34541819e281a68b09357.png

解决方案 

上面的基础逻辑回存在一定的问题,为了避免缓存穿透、缓存雪崩、缓存击穿的问题,可以通过下面方法解决

  • 解决缓存穿透:空结果缓存,比如数据库查询结果为空,则缓存一个特殊值
  • 解决缓存雪崩:设置过期时间 (加随机值),避免缓存同时失效
  • 解决缓存击穿:对操作数据库的逻辑加锁,不能让所有请求都查询数据库

单体架构加锁 (本地锁)

当并发请求时,只让其中一个请求拿到锁以后,去查一次数据库,然后将查询的结果放入缓存中,其他请求就不需要再次查询数据库了,

当一个请求拿到锁以后,需要再次查询缓存,如果没有才继续进行数据库查询。

代码实例如下

577d3ee757e74f31889385ee6adc3f64.png

 锁的时序性问题

4ea76f71a85a49679d37b97f30584580.png

 在并发情况下,当一个请求拿到锁以后,当执行完“确认缓存” 和 “查数据库” 操作后,就会释放锁,当下一个请求获取锁后继续执行“确认缓存” 和 “查数据库” 操作,但此时第一个请求还没有将数据放入缓存成功,就会导致后面的请求重复查询数据库。

所以就需要将“结果放入缓存” 也加到锁中。

8a53e37ed2f5455696853bc0cb0b2988.png

分布式锁

ec297fa4cb94492fb157175838cc80bd.png

 本地锁只能锁住当前进程,在多个服务节点下需要使用分布式锁。

分布式锁的基本原理就是,让过个请求去同一个地方去抢占锁,如果能抢到,则执行业务逻辑,否则就必须等待,直到释放锁,抢锁可以去redis,也可以是数据库,等待可以采用自旋的方式。

f7b982f6b5e44821b80118e8e6591c0f.png死锁 

一个请求进来获取到锁以后,开始执行业务逻辑,如果在业务代码执行的过程中发生异常,后面的删除锁的逻辑没有执行,就会永远锁在这里,造成死锁。

解决方式就是设置锁的自动过期,这样即使没有删除,过期后也会自动删除

d72b27115e1647c49df97e42ba67bc6a.png

事务的原子性

为了避免死锁发生,上面设置的锁的过期时间,但是如果在这是锁的过期时间的过程中有发生了异常、宕机,删除锁的操作依然没有执行,又会出现死锁。

为了避免这种现象,必须将“抢锁”和“设置过期时间” 放到一个事务当中

959d5b13a7244ed6a83e18aa3e0bf9b8.png

删除锁

由于设置了锁的过期时间,在并发请求下,由于业务执行时间比较长,如果直接删除锁,有可能把别人持有的锁给删除了。

8d1697d883d6495a82294a80f4793397.png

 解决办法,可以加一个uuid,在执行删除锁的时候判断一下,自己删除的锁是不是自己从redis中获取的那一个,实例代码如下

8d5e703945424631be4d094fc192bff8.png

删除锁的原子性 

如果程序执行到 “判断是删除的自己的锁” 此时锁正好过期,此时下一个请求获取了新的锁,后面在删除锁的时候,那么删除的就又是别人的锁了。

解决办法就是将“获取当前锁的值”、“判断删除的是否是自己的锁”和“删除锁” 放到一个事务中去,保证原子性

6d7a361a6a5a4c99878d8bba183bd4f1.png

分布式锁Redisson 

上面通过redis 实现的分布式锁比较复杂,如果控制不好就会出现“锁不住”或者“死锁”的问题,Redis 提供了一个更强大的分布式锁,Redisson ,Redisson 的宗旨是促进使用者对redis关注分离,从而将更多精力放到业务处理上.

引入依赖

a45c2a34774347a69d7e09b57b75b1fa.png

 添加配置

参考官方文档

24f88bde522e46e4a7ddb2828d04901c.png

示例代码如下

0562ebdbadd24b3e887afa49e935489a.png

通过redisson,不需要在关心是否出现死锁问题,只需要关注业务逻辑即可

读写锁

redisson提供读写锁,读数据的气候加读锁,写数据的时候加写锁,保证一定能够获取到最新数据,读写锁是同时存在的,例如,在写入数据前加锁,在写入逻辑完成之前,读数据的操作会一直在等待,知道写操作完成后释放锁,才能正常读到最新的数据,示例代码如下

读锁

0c3912c956854d6083026b34e5070398.png

写锁 

7f51c87dfb22461cae9ee2b06ff47109.png

总结

28d2aaaa2d1a49a6a6bc57aff02bcd60.png

闭锁

e2fb112f232e41578292f2d8c98b2c06.png

通过redisson的闭锁,可以实现当gogogo方法调用5次的时候,lockDoor方法才会执行完成

信号量

例如在redis中初始化一个key为park ,value 为3的键值,示例代码中每执行一次go 方法,value值就会减1,当值减到0的时候,park方法返回false ,值大于零的时候park方法返回true ,信号量可以在分布式系统中实现限流的功能,

e19b5fdbc442443089424d1e086d44ab.png

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值