本地缓存模式:不同服务器有着不同的缓存,出现缓存一致性的问题

分布式缓存:缓存统一放到缓存中间件中

为了系统性能的提升,我们一般都会将部分数据放入缓存中,加速访问。而 db 承担数据落盘工作。
哪些数据适合放入缓存?
举例:电商类应用,商品分类,商品列表等适合缓存并加一个失效时间 (根据数据更新频率来定),后台如果发布一个商品,买家需要 5 分钟才能看到新的商品一般还是可以接受的。

data = cache.load(id);//从缓存加载数据
If(data == null){
data = db.load(id);//从数据库加载数据
cache.put(id,data);//保存到 cache 中
}
return data;
注意:在开发中,凡是放入缓存中的数据我们都应该指定过期时间,使其可以在系统即使没有主动更新数据也能自动触发数据加载进缓存的流程。避免业务崩溃导致的数据永久不一致问题。
A、引入 redis-starter
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-redisartifactId>
dependency>
B、配置 redis
spring:
redis:
host: 192.168.56.10
port: 6379
C、使用 RedisTemplate 操作 redis
@Autowired
StringRedisTemplate stringRedisTemplate;
@Test
public void testStringRedisTemplate(){
ValueOperations<String, String> ops = stringRedisTemplate.opsForValue();
ops.set("hello","world_"+ UUID.randomUUID().toString());
String hello = ops.get("hello");
System.out.println(hello);
}


// TODO 产生堆外内存溢出 OutOfDirectMemoryError
// 1) springboot 2.0 之后默认使用 lettuce 作为 操作 redis 的客户端, 他使用 netty 进行网络通信
// 2) lettuce 的 bug 导致 netty 堆外内存溢出 -Xmx 300m netty 如果没有指定堆外内存 默认使用 -Xmx300m 作为堆外内存
// 可以 通过 -Dio.netty.maxDirectMemory 进行设置
// 解决方案:
// 1、升级 lettuce 客户端
// 2、切换使用 jedis
@Override
public Map<String, List<Catelog2Vo>> getCatelogJson() {
// 给缓存中放 json 字符串, 拿出的 json 字符串 , 还能逆转为 能用的对象 [序列化与反序列化]
// 1. 加入缓存逻辑 缓存中存的数据是 json 字符串
// json 跨语言、跨平台兼容
String catelogJSON = redisTemplate.opsForValue().get("catelogJSON");
if (StringUtils.isEmpty(catelogJSON)) {
// 2. 缓存中没有, 查询数据库
Map<String, List<Catelog2Vo>> catelogJsonFromDB = getCatalogJsonFromDB();
// 3. 查到的数据再放入缓存, 将对象转为 json 放在缓存中
String s = JSON.toJSONString(catelogJsonFromDB);
redisTemplate.opsForValue().set("catelogJSON", s);
return catelogJsonFromDB;
}
// 转为指定的对象
Map<String, List<Catelog2Vo>> result = JSON.parseObject(catelogJSON, new TypeReference<Map<String, List<Catelog2Vo>>>(){});
return result;
}
public Map<String, List<Catelog2Vo>> getCatalogJsonFromDB() {
//1. 查出所有 1 级分类
List<CategoryEntity> level1Categorys = getLevel1Categorys();
//2. 封装数据
Map<String, List<Catelog2Vo>> parent_cid = level1Categorys.stream().collect(Collectors.toMap(k->k.getCatId().toString(),v->{
// 1. 每一个一级分类, 查到这个一级分类的二级分类
List<CategoryEntity> categoryEntities = baseMapper.selectList(new QueryWrapper<CategoryEntity>().eq("parent_cid", v.getCatId()));
List<Catelog2Vo> catelog2Vos = null;
if (categoryEntities != null) {
catelog2Vos = categoryEntities.stream().map(l2->{
Catelog2Vo catelog2Vo = new Catelog2Vo(v.getCatId().toString(),null , l2.getCatId().toString(), l2.getName());
// 找当前二级分类的三级分类封装成vo
List<CategoryEntity> level3Catelog = baseMapper.selectList(new QueryWrapper<CategoryEntity>().eq("parent_cid", l2.getParentCid()));
if (level3Catelog != null) {
List<Catelog2Vo.Category3Vo> collect = level3Catelog.stream().map(l3->{
// 封装成指定格式
Catelog2Vo.Category3Vo category3Vo = new Catelog2Vo.Category3Vo(l2.getCatId().toString(),l3.getCatId().toString(), l3.getName());
return category3Vo;
}).collect(Collectors.toList());
catelog2Vo.setCatalog3List(collect);
}
return catelog2Vo;
}).collect(Collectors.toList());
}
return catelog2Vos;
}));
return parent_cid;
}
@Override
public List<CategoryEntity> getLevel1Categorys() {
List<CategoryEntity> categoryEntities = baseMapper.selectList(new QueryWrapper<CategoryEntity>().eq("parent_cid", 0));
return categoryEntities;
}
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-redisartifactId>
<exclusions>
<exclusion>
<groupId>io.lettucegroupId>
<artifactId>lettuce-coreartifactId>
exclusion>
exclusions>
dependency>
<dependency>
<groupId>redis.clientsgroupId>
<artifactId>jedisartifactId>
dependency>