为了系统性能的提升,我们一般都会将部分数据放入缓存中,加速访间。而db承担数据落盘工作。
哪些数据适合放入锾存?
- 即时性、数据—致性要求不高的
- 访问量大且更新频率不高的数据(读多,写少)
举例:电商类应用,商品分类,商品列表等适合缓存并加一个失效时间(根据数据更新频率来定),后台如果发布一个商品,买家需要5分钟才能看到新的商品一般还是可以接受的。
2 本地缓存与分布式缓存 2.1 本地缓存 2.2 分布式缓存-本地模式在分布式下的问题其中一个服务的缓存修改了,但是其他服务里的缓存没有办法更新,无法保证数据的一致性
2.3分布式缓存 3 整合Redis 3.1 引入依赖product模块
3.2 配置redisorg.springframework.boot spring-boot-starter-data-redis
redis: host: 192.168.56.10 port: 63793.3 单元测试
@Test
void test03() {
ValueOperations ops = stringRedisTemplate.opsForValue();
ops.set("hello", "world_" + UUID.randomUUID());
String hello = ops.get("hello");
System.out.println("保存的数据是:" + hello);
}
4 改造三级分类业务
将原来的getCatalogJson方法改名为getCatalogJsonFromDB,并创建一个新的getCatalogJson方法
@Autowired StringRedisTemplate stringRedisTemplate; @Override public Map> getCatalogJson() { // 给缓存中放JSON字符串,拿出的JSON字符串,还需要逆转为能用的对象类型 String catalogJSON = stringRedisTemplate.opsForValue().get("catalogJSON"); if (StringUtils.isEmpty(catalogJSON)) { // 缓存中没有,查询数据库 Map > catalogJsonFromDB = getCatalogJsonFromDB(); // 将查到的数据放入缓存 String s = JSON.toJSONString(catalogJsonFromDB); stringRedisTemplate.opsForValue().set("catalogJSON", s); // 返回查询数据库查到的数据 return catalogJsonFromDB; } // 将缓存中查询出的json转换为指定的对象 Map > result = JSON.parseObject(catalogJSON, new TypeReference
重启服务,访问http://localhost:10000/index/catalog.json测试
5 压力测试内存溢出云服务器的吞吐量低是网络带宽原因。
问题原因:
- SpringBoot2.0以后默认使用Lettuce作为操作redis的客户端。它使用netty进行通信
- Lettuce的bug导致netty堆外内存溢出
- netty如果没有指定堆外内存,会默认使用服务配置里的 -Xmx 作为堆外内存的大小
- 可以通过-Dio.netty.maxDirectMemory进行设置
解决方法:
- 升级 Lettuce客户端
- 切换使用 jedis客户端
1、排除Lettuce依赖包
2、导入jedis依赖
org.springframework.boot spring-boot-starter-data-redis io.lettuce lettuce-core redis.clients jedis
3、重启服务测试
坑:切换为jedis客户端后压力测试时报如下异常nested exception is redis.clients.jedis.exceptions.JedisConnectionException: java.net.SocketTimeoutException: Read timed out] with root cause
问题原因:
我的redis在阿里云服务器上,压力测试时可能由于网速太慢的问题,redis连接可能超时了
解决方法:
配置redis的超时时间
redis: timeout: 1000005.2 redisTemplate和Lettuce的关系
redisTemplate:spring对Lettuce、jedis的再次封装
Lettuce、jedis:操作redis的底层客户端
6 缓存失效问题 6.1 缓存穿透 6.2 缓存雪崩 6.3 缓存击穿 7 解决缓存击穿 7.1 分布式锁 7.2 锁时序图 7.3 本地锁查询数据库给getCatalogJsonFromDB方法加锁,
将getCatalogJson方法中添加缓存的代码转移到getCatalogJsonFromDB里
改进后的getCatalogJson方法
@Override public Map> getCatalogJson() { // 给缓存中放JSON字符串,拿出的JSON字符串,还需要逆转为能用的对象类型 String catalogJSON = stringRedisTemplate.opsForValue().get("catalogJSON"); if (!StringUtils.hasText(catalogJSON)) { // 缓存中没有,查询数据库 return getCatalogJsonFromDB(); } // 将缓存中查询出的json转换为指定的对象 Map > result = JSON.parseObject(catalogJSON, new TypeReference
改进后的getCatalogJsonFromDB方法
// 从数据库查询并封装分类数据 public Map> getCatalogJsonFromDB() { synchronized (this) { // 得到锁以后,在去缓存中确定一次 String catalogJSON = stringRedisTemplate.opsForValue().get("catalogJSON"); if(!StringUtils.isEmpty(catalogJSON)) { Map > result = JSON.parseObject(catalogJSON, new TypeReference



