谷粒商城_09_分布式缓存+分布式锁

  • 本地缓存:和微服务同一个进程。缺点:分布式时
  • 分布式缓存:缓存中间件

在这里插入图片描述

本地缓存

本地缓存问题:每个微服务都要有缓存服务、数据更新时只更新自己的缓存,造成缓存数据不一致

解决方案:分布式缓存,微服务共用 缓存中间件

  • 采用map来存缓存,直接放到内存中,但是本地缓存就会在分布式服务中出现问题,各个服务的缓存都是不一样的

分布式缓存

在这里插入图片描述

在这里插入图片描述

redis

整合redis的步骤

  • 引入依赖
  • 配置redis信息
  • 直接使用spring自动配置的RedisTemplate

1、product导入依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

2、配置redis主机地址

spring:
  redis:
    host: localhost
    port: 6379
    # 可以指定账号密码

3、自动注入了RedisTemplate,优化菜单获取业务getCatalogJson

  • 测试StringRedisTemplate
@Autowired // 注入StringRedisTemplate
StringRedisTemplate stringRedisTemplate;

@Test
public void test(){
   
    // 相当于拿到redis的map
    ValueOperations<String, String> ops = stringRedisTemplate.opsForValue();
    // 保存 set
    // 获取 get
}
  • 业务getCatalogJson加入缓存逻辑
ValueOperations<String, String> ops = stringRedisTemplate.opsForValue();
// 每次从缓存中获取
String catalogJson = ops.get("catalogJson");
// 判断redis中是否有此缓存
if (catalogJson == null) {
   
    // 没有,则从数据库中查询
    Map<String, List<Catalog2Vo>> categoriesDb = getCategoriesDb();
    // 先将数据库的数据转为json在放入缓存中,这也就是【序列化】
    // JSON跨语言,跨平台兼容
    String toJSONString = JSON.toJSONString(categoriesDb);
    // 查到数据放到redis中
    ops.set("catalogJson",toJSONString);
    return categoriesDb;
}
// 如果缓存有,先【反序列化】,再返回
Map<String, List<Catalog2Vo>> listMap = JSON.parseObject(catalogJson, new TypeReference<Map<String, List<Catalog2Vo>>>() {
   });
return listMap;
堆外内存溢出
  • 当进行压力测试时后期后出现堆外内存溢出OutOfDirectMemoryError

产生原因:

1、springboot2.0以后默认使用lettuce作为操作redis的客户端,它使用netty进行网络通信

2、lettuce的bug导致netty堆外内存溢出。netty如果没有指定堆外内存,默认使用Xms的值,可以使用-Dio.netty.maxDirectMemory来设置

解决方案:由于是lettuce的bug造成,不要直接使用-Dio.netty.maxDirectMemory去调大虚拟机堆外内存。

  1. 升级lettuce客户端

  2. 切换使用jedis、导入依赖即可

<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>

lettuce和jedis是操作redis的底层客户端,RedisTemplate是再次封装

压力测试

在这里插入图片描述

缓存失效

缓存穿透

在这里插入图片描述

缓存穿透是指缓存和数据库中都没有的数据,而用户不断发起请求,如发起为id为10000的数据或id为特别大不存在的数据。这时的用户很可能是攻击者,攻击会导致数据库压力过大。

解决方案:

  • 缓存我们的null对象,但是要设置短暂的过期时间,防止以后有这个数据,而缓存中还是缓存的null

缓存雪崩

在这里插入图片描述

缓存雪崩是指在我们设置缓存时key采用了相同的过期时间,导致缓存在某一时刻缓存大面积的同时失效,请求全部转发到DB,DB瞬时压力过重雪崩。

解决方案:

  • 避免雪崩:缓存数据的过期时间设置随机,防止同一时间大量数据过期现象发生。
  • 如果缓存数据库是分布式部署,将热点数据均匀分布在不同缓存数据库中。
  • 设置热点数据永远不过期。
  • 出现雪崩:降级 熔断
  • 事前:尽量保证整个 redis 集群的高可用性,发现机器宕机尽快补上。选择合适的内存淘汰策略。
  • 事中:本地ehcache缓存 + hystrix限流&降级,避免MySQL崩掉
  • 事后:利用 redis 持久化机制保存的数据尽快恢复缓存

缓存击穿

在这里插入图片描述

三者不同的是:

  • 缓存击穿指并发查同一条数据。缓存击穿是指缓存中没有但数据库中有的数据(一般是缓存时间到期),这时由于并发用户特别多,同时读缓存没读到数据,又同时去数据库去取数据,引起数据库压力瞬间增大,造成过大压力
  • 缓存雪崩是不同数据都过期了,很多数据都查不到从而查数据库。
  • 缓存穿透也是指并发查同一条数据,但是穿透是数据库也没有此数据

解决方案:

  • 设置热点数据永远不过期。
  • 加互斥锁

缓存击穿:加锁

本地锁

在这里插入图片描述

1、本地锁synchronized(this)、JUC(Lock)

  • 竞争到锁后,确认缓存中没有,再去查数据库,将数据放入缓存中,再去释放锁。

  • Springboot所有的组件都是单例的,那么锁this就可以把所有的请求都锁住,但是这是单服务上的,如果是多服务就有多个实例,那么synchronized就不管用了

复制微服务

1、右键点击服务,copy configuration

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

早上真起不来!

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值