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


    缓存实战

    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 getTypeEntityListByLock() {
      synchronized (this) {
        // 1.从缓存中查询数据
        String typeEntityListCache = stringRedisTemplate.opsForValue().get("typeEntityList");
        if (!StringUtils.isEmpty(typeEntityListCache)) {
          // 2.如果缓存中有数据,则从缓存中拿出来,并反序列化为实例对象,并返回结果
          List typeEntityList = JSON.parseObject(typeEntityListCache, new TypeReference>(){});
          return typeEntityList;
        }
        // 3.如果缓存中没有数据,从数据库中查询数据
        System.out.println("The cache is empty");
        List typeEntityListFromDb = this.list();
        // 4.将从数据库中查询出的数据序列化 JSON 字符串
        typeEntityListCache = JSON.toJSONString(typeEntityListFromDb);
        // 5.将序列化后的数据存入缓存中,并返回数据库查询结果
        stringRedisTemplate.opsForValue().set("typeEntityList", typeEntityListCache, 1, TimeUnit.DAYS);
        return typeEntityListFromDb;
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    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

  • 相关阅读:
    GAN Step By Step -- Step2 GAN的详细介绍及其应用
    C. Friends and Gifts
    【Zookeeper客户端常用的命令&&Zookeeper的核心功能之节点数据存储】
    做测试8年,33岁前只想追求大厂高薪,今年只求稳定收入
    【花雕动手做】有趣好玩的音乐可视化系列项目(31)--LCD1602液晶屏
    1-EleasticSearch高可用集群核心原理
    Redis数据类型
    (十二)51单片机----用DS18B20浅测一下工(江)西的室外温度
    【NodeJs入门学习】node服务环境搭建
    css问题
  • 原文地址:https://blog.csdn.net/qq_36151389/article/details/132868817