【谷粒商城】高级篇-分布式锁与缓存

本文探讨了Spring Cache在项目中的应用及其与分布式缓存Redis的结合,包括缓存策略、一致性问题及Redisson分布式锁的使用,帮助理解缓存技术在微服务架构中的关键角色和优化技巧。

请直接ctrl+F搜索内容

缓存

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

1. 本地缓存

category、brand、product

redis

redis学习笔记:https://blog.youkuaiyun.com/hancoder/article/details/105694186

安装docker-redis

product导入依赖

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

配置redis主机地址

spring:
  redis:
    host: 192.168.56.10
    port: 6379

自动注入了RedisTemplate

优化菜单获取业务getCatalogJson

ValueOperations<String, String> ops = stringRedisTemplate.opsForValue();
String catalogJson = ops.get("catalogJson");
if (catalogJson == null) {
   
   
    Map<String, List<Catalog2Vo>> categoriesDb = getCategoriesDb();
    String toJSONString = JSON.toJSONString(categoriesDb);
    ops.set("catalogJson",toJSONString);
    return categoriesDb;
}
Map<String, List<Catalog2Vo>> listMap = JSON.parseObject(catalogJson, new TypeReference<Map<String, List<Catalog2Vo>>>() {
   
   });
return listMap;
lettuce堆外内存溢出bug

当进行压力测试时后期后出现堆外内存溢出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是再次封装

3) 缓存失效

缓存穿透

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

解决:缓存空对象、布隆过滤器、mvc拦截器

缓存雪崩

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

解决方案:

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

缓存击穿

缓存雪崩和缓存击穿不同的是:

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

解决方案:

  • 设置热点数据永远不过期。
  • 加互斥锁:业界比较常用的做法,是使用mutex。简单地来说,就是在缓存失效的时候(判断拿出来的值为空),不是立即去load db去数据库加载,而是先使用缓存工具的某些带成功操作返回值的操作(比如Redis的SETNX或者Memcache的ADD)去set一个mutex key,当操作返回成功时,再进行load db的操作并回设缓存;否则,就重试整个get缓存的方法。
4) 缓存击穿:加锁

不好的方法是synchronized(this),肯定不能这么写 ,不具体写了

锁时序问题:之前的逻辑是查缓存没有,然后取竞争锁查数据库,这样就造成多次查数据库。

解决方法:竞争到锁后,再次确认缓存中没有,再去查数据库。

如何复制微服务:

右键点击服务,copy configuration

program arguments: --server.port=10003

2. 分布式缓存

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

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

分布式锁

分布式项目时,但本地锁只能锁住当前服务,需要分布式锁

redis分布式锁的原理:setnx,同一时刻只能设置成功一个

前提,锁的key是一定的,value可以变

  • 没获取到锁阻塞或者sleep一会

  • 设置好了锁,玩意服务出现宕机,没有执行删除锁逻辑,这就造成了死锁

    • 解决:设置过期时间
  • 业务还没执行完锁就过期了,别人拿到锁,自己执行完去删了别人的锁

    • 解决:锁续期(redisson有看门狗),。删锁的时候明确是自己的锁。如uuid
  • 判断uuid对了,但是将要删除的时候锁过期了,别人设置了新值,那删除了别人的锁

    • 解决:删除锁必须保证原子性(保证判断和删锁是原子的)。使用redis+Lua脚本完成,脚本是原子的

    • if redis.call("get",KEYS[1]) == ARGV[1] 
      then
      	return redis.call("del",KEYS[1])
      else
          return 0
      end;
      
    • stringRedisTemplate.execute(
          new DefaultRedisScript<Long返回值类型>(script脚本支付非常, Long.class返回值类型),
评论 14
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值