缓存与分布式锁(Redis 和 springCache)

参考地址(Redis的基本使用)

https://blog.csdn.net/suchahaerkang/article/details/108561366

在使用redis进行开发的,出现OutOfDirectMemoryError堆外内存溢出

2021-05-29 14:10:28.509 WARN 18020 — [ioEventLoop-4-1] io.lettuce.core.protocol.CommandHandler : null Unexpected exception during request: io.netty.util.internal.OutOfDirectMemoryError: failed to allocate 46137344 byte(s) of direct memory (used: 58720256, max: 100663296)
使用58720256,需要释放空间46137344 ,但是最大内存100663296 (46137344 +58720256 >100663296 )

产生堆外内存溢出OutOfDirectMemoryError:
1)、springboot2.0以后默认使用lettuce操作redis的客户端,它使用netty进行与redis的网络通信
2)、lettuce的bug导致netty堆外内存溢出 -Xmx300m; netty如果没有指定堆外内存,默认使用-Xmx300m 可设置:
-Dio.netty.maxDirectMemory
解决方案:不能直接使用-Dio.netty.maxDirectMemory去调大堆外内存
当你调大 -Xmx300m运行内存空间时,只是减缓了OutOfDirectMemoryError异常出现的时间
1)、升级lettuce客户端。(项目推荐使用,需要手动解决该问题。lettuce在使用netty进行通信时,没有及时释放空间导致内存溢出,但是netty吞吐量达,新,所以推荐使用)
2)、切换使用jedis(比较老的版本)

使用Redis缓存性能发生大的改变

在这里插入图片描述

高并发下缓存失效的问题

(读模式)缓存穿透, 缓存雪崩 缓存击穿

穿透:查询一个永不存在的数据
雪崩:缓存key同一时间大面积失效
击穿:某一个单热点被高频访问,在前一点突然失效。

解决方案:
1、空结果缓存:解决缓存穿透
2、设置过期时间(加随机值),解决缓存雪崩
3、加锁:解决缓存击穿

解决缓存击穿,进行加锁

1. 本地锁
synchronized关键字:表明这是一个加锁的操作。
锁:其实就是一个对象。只要是同一把锁,就能锁住这个锁的所有线程换句话说就是只要是用同一个对象锁,那么调用这个锁对象的所有进程都能锁住。

本地锁只能是在单体应用可以使用,如果是高并发的情况下,假如10个机器,就有10把锁。
synchronized关键字修饰的锁和JUC(Lock)都是本地锁,只能锁住当前进程
在这里插入图片描述
锁时序问题
黑色框表示锁
在这里插入图片描述

2. 分布式锁
分布式的情况下,锁住所有的进程。

本地锁:当是单体应用的时候,使用的是本地锁,例如JUC,里面有各种锁,比如可重入锁等。
分布式锁:当是多个应用的时候,这个时候要锁住所有应用的进程,需要Redis提供的分布式锁,Redisson对各种锁进行封装,可以使用简单的代码进行加锁。

在这里插入图片描述
我们可以同时去一个地方“占坑”,如果占到,就执行逻辑。否则就必须等待,直到释放锁。占坑可以去Redis,也可以去数据库,可以去任何大家都可以访问到的地方。等待可以自旋的方式。

使用锁的过程中注意的方式:
1.加锁和设置时间保持原子性
2. 删除锁,保证删除自己的锁(1.去redis获取lock锁的值 2.将获取的值与当前uuid比较,相等则删除)
步骤1 + 步骤2 ==保持原子性 ,使用redis官方提供的脚本运行。
3.让锁能够自动续机:当业务执行代码时间过长,还没执行完业务代码,锁就到期了,需要自动续机。
解决方案:(1)设置锁的时间长一点,加上try(业务代码)finally{删锁},无论业务代码是否有问题,都要删锁。
(2) 。。。。

分布式锁-Redisson(锁框架,封装了各种分布式锁)

1.可重入锁

2.读写锁

3.信号量

4.分布式闭锁

写模式(缓存数据一致性问题)

双写模式和失效模式

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

Canal,阿里开源,缓存数据一致性解决方案

Canal是一个阿里开源的一个组件,用来解决缓存数据和数据库数据一致性问题而开发的

在这里插入图片描述

Spring Cache

spring cache提供了对缓存技术的crud操作,缓存技术可以使用Redis,Map等等

在这里插入图片描述
在这里插入图片描述

步骤

1、引入依赖
spring-boot-starter-cache,spring-boot-starter-data-redis
2、写配置
2.1 自动配置了那些 (CacheAutoConfiguration 会导入 RedisCacheConfiguration,自动配好了缓存管理器)
2.2 配置使用Redis作为缓存 spring.cache.type=redis
3、使用缓存
3.1 在启动类上@EnableCaching 注解 开启缓存
3.2 使用相关Cache注解实现缓存操作

SPEL操作文档

https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/integration.html#cache-spel-context

使用缓存注解

在这里插入图片描述

spring cache原理

如果容器中的一个组件,只有一个构造器,那么其构造器的参数都是有容器提供的

自动配置类CacheAutoConfiguration —》 导入RedisCacheConfiguration 配置 —》自动配置了RedisCacheManager —》初始化所有缓存 —》每个缓存决定使用什么配置—》如果有redisCacheConfiguration就用已有的,没有就用默认配置 —》想改缓存配置,就给容器中放一个RedisCacheConfiguration即可 —》就会应用到当前RedisCacheManager 管理的所有的缓存分区中。

/**
     * 每一个需要缓存的数据我们都来指定要放到那个名字的缓存。【缓存的分区(按照业务类型分)】
     * 代表当前方法的结果需要缓存,如果缓存中有,方法都不用调用,如果缓存中没有,会调用方法。最后将方法的结果放入缓存
     * 默认行为
     *      如果缓存中有,方法不再调用
     *      key是默认生成的: 缓存的名字::SimpleKey::[] (自动生成key值),配置文件指定前缀的话,则是:前缀SimpleKey::[]
     *      缓存的value值,默认使用jdk序列化机制,将序列化的数据存到redis中
     *      默认时间是 -1:永不过期
     *
     * 自定义操作:key的生成
     *      指定生成缓存的key:key属性指定,接收一个Spel表达式
     *      指定缓存的数据的存活时间:配置文档中修改存活时间
     *      将数据保存为json格式:保证如果使用的是PHP语言等也能使用,自定义自己的配置文件MyCacheConfig
     *          1.缓存的自动配置CacheAutoConfiguration
     *          2.RedisCacheConfigration
     *
     *
     * 4、Spring-Cache的不足之处:
     *  1)、读模式
     *      缓存穿透:查询一个null数据。解决方案:缓存空数据
     *      缓存击穿:大量并发进来同时查询一个正好过期的数据。解决方案:加锁 ? 默认是无加锁的;使用sync = true来解决击穿问题
     *      缓存雪崩:大量的key同时过期。解决:加随机时间。加上过期时间
     *  2)、写模式:(缓存与数据库一致)
     *      1)、读写加锁。
     *      2)、引入Canal,感知到MySQL的更新去更新Redis
     *      3)、读多写多,直接去数据库查询就行
     *
     *  总结:
     *      常规数据(读多写少,即时性,一致性要求不高的数据,完全可以使用Spring-Cache):写模式(只要缓存的数据有过期时间就足够了)
     *      特殊数据:特殊设计
     *
     *  原理:
     *      CacheManager(RedisCacheManager)->Cache(RedisCache)->Cache负责缓存的读写
     *
     */

spring Cache 不足

4、Spring-Cache的不足之处:
* 1)、读模式
* 缓存穿透:查询一个null数据。解决方案:缓存空数据 ==》配置文件:spring.cache.redis.cache-null-values=true
* 缓存击穿:大量并发进来同时查询一个正好过期的数据。解决方案:加锁 ? 默认是无加锁的;使用sync = true来解决击穿问题
* 缓存雪崩:大量的key同时过期。解决:加随机时间。加上过期时间
* 2)、写模式:(缓存与数据库一致)
* 1)、读写加锁。
* 2)、引入Canal,感知到MySQL的更新去更新Redis
* 3)、读多写多,直接去数据库查询就行
*
* 总结:
* 常规数据(读多写少,即时性,一致性要求不高的数据,完全可以使用Spring-Cache):写模式(只要缓存的数据有过期时间就足够了)
* 特殊数据:特殊设计
*
* 原理:
* CacheManager(RedisCacheManager)->Cache(RedisCache)->Cache负责缓存的读写

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值