对缓存穿透、雪崩、击穿的理解,引入分布式锁

缓存实战

1、缓存穿透

先来了解一个小图,

1.1 概念:

缓存穿透指一个一定不存在的数据,由于缓存未命中这条数据,就会去查询数据库,数据库也没有这条数据,所以返回结果是 null。如果每次查询都走数据库,则缓存就失去了意义,就像穿透了缓存一样。

在这里插入图片描述

1.2 带来的风险

利用不存在的数据进行攻击,数据库压力增大,最终导致系统崩溃。

1.3 解决方案

对结果 null 进行缓存,并加入短暂的过期时间。

2、缓存雪崩

2.1 概念

缓存雪崩是指我们缓存多条数据时,采用了相同的过期时间,比如 00:00:00 过期,如果这个时刻缓存同时失效,而有大量请求进来了,因未缓存数据,所以都去查询数据库了,数据库压力增大,最终就会导致雪崩。
在这里插入图片描述

2.2 带来的风险

尝试找到大量 key 同时过期的时间,在某时刻进行大量攻击,数据库压力增大,最终导致系统崩溃。

2.3 解决方案

在原有的失效时间基础上增加一个随机值,比如 1-5 分钟随机,降低缓存的过期时间的重复率,避免发生缓存集体失效。

3、缓存击穿

3.1 概念

某个 key 设置了过期时间,但在正好失效的时候,有大量请求进来了,导致请求都到数据库查询了。
在这里插入图片描述

3.2 解决方案

大量并发时,只让一个请求可以获取到查询数据库的锁,其他请求需要等待,查到以后释放锁,其他请求获取到锁后,先查缓存,缓存中有数据,就不用查数据库。

4、 加锁解决缓存击穿

怎么处理缓存穿透、雪崩、击穿的问题呢?
对空结果进行缓存,用来解决缓存穿透问题。
设置过期时间,且加上随机值进行过期偏移,用来解决缓存雪崩问题。
加锁,解决缓存击穿问题。另外需要注意,加锁对性能会带来影响。
这里我们来看下用代码演示如何解决缓存击穿问题。
我们需要用 synchronized 来进行加锁。当然这是本地锁的方式

public List<TypeEntity> getTypeEntityListByLock() {
  synchronized (this) {
    // 1.从缓存中查询数据
    String typeEntityListCache = stringRedisTemplate.opsForValue().get("typeEntityList");
    if (!StringUtils.isEmpty(typeEntityListCache)) {
      // 2.如果缓存中有数据,则从缓存中拿出来,并反序列化为实例对象,并返回结果
      List<TypeEntity> typeEntityList = JSON.parseObject(typeEntityListCache, new TypeReference<List<TypeEntity>>(){});
      return typeEntityList;
    }
    // 3.如果缓存中没有数据,从数据库中查询数据
    System.out.println("The cache is empty");
    List<TypeEntity> typeEntityListFromDb = this.list();
    // 4.将从数据库中查询出的数据序列化 JSON 字符串
    typeEntityListCache = JSON.toJSONString(typeEntityListFromDb);
    // 5.将序列化后的数据存入缓存中,并返回数据库查询结果
    stringRedisTemplate.opsForValue().set("typeEntityList", typeEntityListCache, 1, TimeUnit.DAYS);
    return typeEntityListFromDb;
  }
}

1.从缓存中查询数据。

2.如果缓存中有数据,则从缓存中拿出来,并反序列化为实例对象,并返回结果。

3.如果缓存中没有数据,从数据库中查询数据。

4.将从数据库中查询出的数据序列化 JSON 字符串。

5.将序列化后的数据存入缓存中,并返回数据库查询结果。

5、本地锁的问题

本地锁只能锁定当前服务的线程,如下图所示,部署了多个题目微服务,每个微服务用本地锁进行加锁。
在这里插入图片描述
本地锁在一般情况下没什么问题,但是在某些情况下就会出问题:

比如在高并发情况下用来锁库存就有问题了:

1.比如当前总库存为 100,被缓存在 Redis 中。

2.库存微服务 A 用本地锁扣减库存 1 之后,总库存为 99。

3.库存微服务 B 用本地锁扣减库存 1 之后,总库存为 99。

4.那库存扣减了 2 次后,还是 99,就超卖了 1 个。

那如何解决本地加锁的问题呢?请看下一篇:
https://editor.csdn.net/md?not_checkout=1&articleId=132869070

猜你喜欢

转载自blog.csdn.net/qq_36151389/article/details/132868817