缓存穿透、雪崩、击穿
- 一、三大问题
- 1.缓存穿透
- 2.缓存雪崩
- 3.缓存击穿
- 4.怎么办?
- 二、锁
- 1.本地锁
- 2.分布式锁
- 3. 本地锁在分布式下怎样呢?
- 1).复制多个product启动类,实现product集群
- 2).启动4个product
- 3).压力测试
- 4). 总结
一、三大问题
1.缓存穿透
2.缓存雪崩
3.缓存击穿
4.怎么办?
穿透:空结果缓冲(null)
雪崩:设置过期时间(随机值)
击穿:加锁
二、锁
1.本地锁
synchronized (this) ,JUC(lock)
synchronized (this) 可以锁代码块、锁方法
//从redis缓存获得数据
//todo: OutOfDirectMemoryError,堆外内存溢出
@Override
public Map<String, List<Category2Vo>> getCatalogJson() {
//把数据放入redis,从redis获得数据[序列化,反序列化],因为JSON跨平台兼容
//1.从redis缓存获得数据
String catalogJson = redisTempalte.opsForValue().get("catalogJson");
//2.如果redis缓存有数据,使用缓存数据;否则查询数据库,再把数据放入redis缓存,并且转为json数据
if (StringUtils.isEmpty(catalogJson)) {
Map<String, List<Category2Vo>> catalogJsonFromDb = getCatalogJsonFromDb();
System.out.println("缓存不命中。。。。将要查询数据库。。");
return catalogJsonFromDb;
}
System.out.println("缓存命中。。。。直接返回。。");
//返回值变为需要的map数据
Map<String, List<Category2Vo>> result = JSON.parseObject(catalogJson, new TypeReference<Map<String, List<Category2Vo>>>() {
});
return result;
}
//2.获取二级、三机分类(从数据库查询并封装分类数据)
public Map<String, List<Category2Vo>> getCatalogJsonFromDb() {
synchronized (this) {
//1.从redis缓存获得数据
String catalogJson = redisTempalte.opsForValue().get("catalogJson");
if (!StringUtils.isEmpty(catalogJson)) {
Map<String, List<Category2Vo>> result =
JSON.parseObject(catalogJson, new TypeReference<Map<String, List<Category2Vo>>>() {});
return result;
}
System.out.println("查询数据库。。。。。"+Thread.currentThread());
//将数据库的多次查询, 变成一次
//nginx动静分离
List<CategoryEntity> selectList = baseMapper.selectList(null);
//1).查询所有一级分类
List<CategoryEntity> categorys1 = getParent_cid(selectList, 0L);
//2).封装数据
Map<String, List<Category2Vo>> parent_cid = categorys1.stream().collect(Collectors.toMap(k -> k.getCatId().toString(), v -> {
//2.1查询每个一级分类的二级分类(k,v都为某个一级分类)
List<CategoryEntity> categoryEntities = getParent_cid(selectList, v.getCatId());
//2.2封装上面结果 (item2,item3为二级、三机分类)
List<Category2Vo> category2Vos = null;
if (categoryEntities != null) {
category2Vos = categoryEntities.stream().map(item2 -> {
Category2Vo category2Vo = new Category2Vo(v.getCatId().toString(), null, item2.getCatId().toString(), item2.getName());
//2.2.1 封装二级分类的三机分类
List<CategoryEntity> level3category = getParent_cid(selectList, item2.getCatId());
//2.2.2 把三机分类数据封装到Category3Vo,并且封装到二级分类Category2Vo
if (level3category != null) {
List<Category2Vo.Category3Vo> collect = level3category.stream().map(item3 -> {
Category2Vo.Category3Vo category3Vo = new Category2Vo.Category3Vo(item2.getCatId().toString(), item3.getCatId().toString(), item3.getName());
return category3Vo;
}).collect(Collectors.toList());
category2Vo.setCatalog3List(collect);
}
return category2Vo;
}).collect(Collectors.toList());
}
return category2Vos;
}));
//2.把查到的数据放入缓存,序列化
String s = JSON.toJSONString(parent_cid);
redisTempalte.opsForValue().set("catalogJson", s, 1, TimeUnit.DAYS);
return parent_cid;
}
}
图片
2.分布式锁
3. 本地锁在分布式下怎样呢?
本地锁:synchronized ,lock
分布式锁:
1).复制多个product启动类,实现product集群
2).启动4个product
3).压力测试
4). 总结
分布式下,使用本地锁,锁不住所有服务(实例)