文章目录
一,153-缓存-缓存使用-改造三级分类业务
这一节的主要内容是改造查询三级分类的接口,将Redis缓存加入进来。
在查询三级分类时,先查redis,如果redis中有数据,则使用redis的数据,如果没有再查数据库,并将结果保存在redis中供后续请求使用。
c
private List<CategoryEntity> getCategoryCache() {
long l = System.currentTimeMillis();
String catelogJson = stringRedisTemplate.opsForValue().get(BasicTypeConstants.CATELOG_JSON);
log.info("查询redis结束{}", System.currentTimeMillis() - l);
List<CategoryEntity> cateList;
if (StrUtil.isEmpty(catelogJson)) {
//将数据库的多次查询变为一次
cateList = this.baseMapper.selectList(null);
stringRedisTemplate.opsForValue().set(BasicTypeConstants.CATELOG_JSON, JSON.toJSONString(cateList));
} else {
cateList = JSON.parseObject(catelogJson, new TypeReference<List<CategoryEntity>>() {});
}
log.info("查询结束{}", System.currentTimeMillis() - l);
return cateList;
}
二,154-缓存-缓存使用-压力测试出的内存泄露及解决
在压测的过程中,可能会出现如下错误。
这是由于Spring Boot 2.0以后默认使用的Lettuce客户端所引发的一种内存溢出问题。
Lettuce客户端的引入
自Spring Boot 2.0版本起,Lettuce成为了操作Redis的默认客户端。Lettuce是基于Netty的网络通信框架实现的,它提供了异步的Redis操作能力,可以显著提高应用程序的性能。
堆外内存溢出问题
在使用Lettuce客户端时,可能会遇到OutOfDirectMemoryError
错误。这是因为Lettuce使用Netty进行网络通信,而Netty默认的堆外内存设置可能不足以满足应用程序的需求。
问题分析
问题原因
-
Lettuce的Bug:Lettuce客户端存在一个Bug,导致Netty在使用默认的堆外内存设置时,可能会发生内存溢出。
-
默认堆外内存设置 :Netty如果没有指定堆外内存,默认使用
-Xmx300m
,这可能不足以应对高并发场景。
解决方案
方案一:调整堆外内存
可以通过设置系统属性-Dio.netty.maxDirectMemory
来增加Netty的堆外内存。但这种方法并不是最佳实践,因为它只是简单地增加内存,而没有解决根本问题。
方案二:切换到Jedis客户端
-
升级Lettuce客户端:如果问题是由Lettuce的Bug引起的,可以尝试升级到最新版本的Lettuce客户端。
-
切换到Jedis:如果升级Lettuce客户端后问题仍然存在,可以考虑切换回Jedis客户端。Jedis是Spring Boot之前默认使用的Redis客户端,它没有使用Netty,因此不会遇到堆外内存溢出的问题。在redis的starter中排除对lettuce的依赖,然后引入jedis的依赖。
c
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<exclusions>
<exclusion>
<groupId>io.lettuce</groupId>
<artifactId>lettuce-core</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
三,155-缓存-缓存使用-缓存击穿、穿透、雪崩
Redis在实际使用过程中,可能会遇到缓存击穿、缓存穿透和缓存雪崩等问题,这些问题严重影响了缓存的效率和系统的稳定性。
缓存击穿(Cache Breakthrough)
问题描述
缓存击穿是指一个非常热点的数据在某个时间点过期时,大量请求直接打在数据库上,导致数据库瞬时压力过大。
解决方案
- 设置过期时间:为缓存数据设置合理的过期时间,避免同时过期。
- 互斥锁:在缓存失效时,通过互斥锁保证同一时间只有一个请求能够访问数据库,其他请求等待或重试。
- 预热机制:在缓存数据即将过期前,提前更新缓存数据。
缓存穿透(Cache Penetration)
问题描述
缓存穿透是指查询一个不存在的数据,由于缓存和数据库中都没有,导致请求直接穿透缓存查询数据库。
解决方案
- 布隆过滤器:使用布隆过滤器拦截不存在的数据请求,避免对数据库的查询。
- 缓存空值:对于查询结果为空的操作也进行缓存,但可以设置较短的过期时间。
- 限制访问频率:对用户访问频率进行限制,避免恶意攻击。
缓存雪崩(Cache Avalanche)
问题描述
缓存雪崩是指在某一时刻,大量的缓存数据同时过期,导致大量请求直接访问数据库,造成数据库压力过大。
解决方案
- 分散缓存过期时间:使用随机时间或分片策略来分散缓存的过期时间,避免同时过期。
- 限流降级:在系统访问量剧增时,通过限流和降级策略保护系统。
- 高可用架构:使用Redis集群,提高缓存系统的可用性和容错性。
实例分析
缓存击穿示例
假设一个电商平台的商品详情页非常热门,缓存设置为10分钟过期。当缓存过期时,如果大量用户同时访问该页面,会导致数据库压力剧增。解决方案是在缓存数据中设置互斥锁,确保同一时间只有一个请求访问数据库。
缓存穿透示例
在电商平台搜索一个不存在的商品,如果没有布隆过滤器,每次搜索都会查询数据库,这会给数据库带来不必要的压力。解决方案是使用布隆过滤器拦截这些请求,避免对数据库的查询。
缓存雪崩示例
电商平台的促销活动导致大量商品缓存在某一时刻同时过期,如果此时用户访问量激增,数据库将承受巨大压力。解决方案是使用Redis集群分散缓存过期时间,并通过限流降级策略保护系统。