一:缓存
1.缓存使用
1)频繁访问数据库,势必会对数据库造成很大的压力,降低系统性能。所以我们可以把不会经常变化的,或者变化对用户影响不大的数据保存到缓存中。
2)为了系统性能的提升,我们一般都会将部分数据放入缓存中,加速访问。而 db 承担数据落盘以及持久化的工作。
3)哪些数据适合放入缓存?
- 即时性、数据一致性要求不高的
- 访问量大且更新频率不高的数据(读多,写少)
4)举例:电商类应用,商品分类,商品列表等适合缓存并加一个失效时间(根据数据更新频率 来定),后台如果发布一个商品,买家需要 5 分钟才能看到新的商品一般还是可以接受的。
2.读模式缓存使用流程
1)流程图
2)代码实现
data = cache.load(id);//从缓存加载数据
If(data == null){
data = db.load(id);//从数据库加载数据
cache.put(id,data);//保存到 cache 中
}
return data;
3)在开发中,凡是放入缓存中的数据我们都应该指定过期时间,使其可以在系统即使没 有主动更新数据也能自动触发数据加载进缓存的流程。避免业务崩溃导致的数据永久不一致 问题。
3.本地缓存——map集合
本地缓存:同一个进程,同一个jvm,在本地保存一个副本。我们可以使用map作为本地缓存,数据以内存的方式进行保存
1)示例代码
private Map<String,Object> cache = new HashMap<>();
//先从缓存中获取数据,如果缓存有,就是用缓存的
Object data = cache.get("msg");
if(data == null){
//首先从数据库获取
Objetc data = db.get("msg");
//其次保存到map中
cache.put("msg",data );
}
return data;
- 如果缓存有,就是用缓存的
- 如果没有,就先从数据库获取,然后保存到缓存中
2)优点&缺点
优点:如果项目是单体架构,只部署在一台服务器,不会有任何问题,使用本地缓存效果很好,很快。
缺点:在分布式系统下,一个模块可能部署在若干服务器上,每个服务器都带一个本地缓存,如果不能负载均衡到一个服务器就会导致,访问到不同服务器时,会重新查询数据库。并且修改数据后,不能保证每一个服务器内的缓存一致,会发生数据不一致的情况。
- 本地缓存
- 分布式缓存
结论:所以在分布式的系统下,不要使用本地缓存,推荐将缓存提取出来,作为一个中间件。都集中的给一个地方存放数据,就会保证数据的一致性问题。
二:SpringBoot整合redis
1.redis的好处
1)使用redis作为集群,分片存储,提升存储容量
2)打破了本地缓存的容量限制
3)易于维护,容易做到高可用,高性能
2.整合redis
1)服务器安装redis
服务器安装redis传送门
2)在product的pom文件中引入redis依赖
<!--引入redis-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
3)在application.yml文件配置redis
spring:
## 指定redis的地址
redis:
## 虚拟机或者服务器的的ip
host: 127.0.0.1
port: 6389
4)使用SpringBoot自动配置好的StringRedisTemplate来操作redis
- redis主要是K,V结构
- StringRedisTemplate的k和v都是字符串
- redis可以当作map来使用,存放数据用key,数据值用value
5)在test类测试redis
@Autowired
private StringRedisTemplate stringRedisTemplate;
@Test
public void testStringRedisTemplate(){
//key-hello value-word
ValueOperations<String, String> ops = stringRedisTemplate.opsForValue();
//保存
ops.set("hello","word_"+ UUID.randomUUID().toString());
//查询
String hello = ops.get("hello");
三:改造三级分类业务-将三级分类信息存入缓存
1.代码逻辑改造
@Override
public Map<String, List<Catalog2Vo>> getCatalogJson() {
//1.加入缓存逻辑,缓存中存入的数据是json字符串
String catalogJson = redisTemplate.opsForValue().get("catalogJson");
if(StringUtils.isEmpty(catalogJson)){
//2.缓存没有就查询数据库
Map<String, List<Catalog2Vo>> catalogJsonFromDb = getCatalogJsonFromDb();
//3.将查到的数据放进缓存,将对象转化为json放进缓存
String s = JSON.toJSONString(catalogJsonFromDb);
redisTemplate.opsForValue().set("catalogJson",s);
return catalogJsonFromDb;
}
//转为指定的对象
TypeReference<Map<String, List<Catalog2Vo>>> typeReference = new TypeReference<Map<String, List<Catalog2Vo>>>() {};
Map<String, List<Catalog2Vo>> result = JSON.parseObject(catalogJson,typeReference);
return result;
}
- JSON跨语言,跨平台的兼容
- 加入缓存逻辑,缓存中的数据就是json字符串
- 给缓存中放入json字符串。拿出的json字符串,还要逆转为能用的对象类型(序列化与反序列化)
2.访问商城首页测试
成功存入redis
三:压力测试出的内存泄漏与解决
1.压力测试
用JMeter模拟高并发下访问三级分类,就会报错!
2.原因分析——堆外内存溢出
1)springboot2.0版本以后,默认使用lettuce作为操作redis的客户端,他使用netty进行网络通信。
2)lettuce的bug导致netty对外内存溢出。netty如果没有指定堆外内存,默认使用-Xmx300m
3)netty底层会统计内存的使用量,使用了多少就会减去多少,直到发生堆外内存溢出
3.解决方案
注:不能通过使用-Dio.netty.maxDirectMemory只去调大堆外内存
1)升级lettuce客户端,使用netty进行网络底层框架,吞吐量极大
2)切换使用jedis客户端,老版客户端,好久没更新
4.引入jedis
<!--引入redis-->
<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>
- 切换jedis客户端以后,吞吐量提高了四倍,响应时间提高了四倍
- 上线之后会切换到lettuce,然后定位问题
5.springRedisTemplate与lettuce,jedis之间的关系
- lettuce,jedis都是操作redis的底层客户端
- spring对lettuce,jedis会再次封装为springRedisTemplate
- springRedisTemplate可以直接操作lettuce和jedis
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/84107.html