• redis 缓存雪崩、缓存击穿、缓存穿透


    一、缓存雪崩

    描述

    大量的key设置了相同的过期时间,导致在缓存在同一时刻全部失效,造成瞬时DB请求量大、压力骤增,引起雪崩。

    简单的例子
    目前电商首页以及热点数据都会去做缓存 ,一般缓存都是定时任务去刷新,或者是查不到之后去更新的,定时任务刷新就有一个问题。如果所有首页的Key失效时间都是12小时,中午12点刷新的,零点有个秒杀活动大量用户涌入,假设当时每秒 6000 个请求,本来缓存在可以扛住每秒 5000 个请求,但是缓存当时所有的Key都失效了。此时 1 秒 6000 个请求全部落数据库,数据库必然扛不住,它会报一下警,真实情况可能DBA都没反应过来就直接挂了。此时,如果没用什么特别的方案来处理这个故障,DBA 很着急,重启数据库,但是数据库立马又被新的流量给打死了。
    解决方案
     给缓存设置过期时间时加上一个随机值时间,使得每个key的过期时间分布开来,不会集中在同一时刻失效。
      
    setRedis(Key,value,time + Math.random() * 10000)

    如果Redis是集群部署,将热点数据均匀分布在不同的Redis库中也能避免全部失效的问题,或者设置热点数据永远不过期,有更新操作就更新缓存就好了(比如运维更新了首页商品,那你刷下缓存就完事了,不要设置过期时间),电商首页的不频繁更新的数据也可以用这个操作。

    二、缓存穿透

    描述:

    访问一个不存在的key,缓存不起作用,请求会穿透到DB,同时数据库也没有相应数据,流量大时DB会挂掉

    简单的例子:

    用户不断发起请求,数据库的 id 都是1开始自增上去的,如发起为id值为 -1 的数据或 id 为特别大不存在的数据。这时的用户很可能是攻击者,攻击会导致数据库压力过大,严重会击垮数据库。

     解决方案:

    1、在接口层增加校验,比如用户鉴权校验,参数做校验,不合法的参数直接代码Return,比如:id 做基础校验,id <=0的直接拦截

    2、采用布隆过滤器,使用一个足够大的bitmap,用于存储可能访问的key,不存在的key直接被过滤;

    3、访问key未在DB查询到值,也将空值写进缓存,但可以设置较短过期时间。

    三、缓存击穿

    描述:

    一个Key非常热点,在不停的扛着大并发,大并发集中对这一个点进行访问,在缓存过期的一刻,同时有大量的请求,这些请求都会击穿到DB,造成瞬时DB请求量大、压力骤增。

    解决方案:

    在访问key之前,采用SETNX(set if not exists)来设置另一个短期key来锁住当前key的访问,访问结束再删除该短期key。此处采用redlock加锁。

    const Redis = require("ioredis");
    const Redlock = require('redlock');
    const redis = new Redis({
        port: 6379, // Redis port
        host: "127.0.0.1", // Redis host
        db: 13,
      });
    const redlock = new Redlock(
        [redis],
        {
            driftFactor: 0.01, // time in ms
            retryCount:  10,
            // the time in ms between attempts
            retryDelay:  200, // time in ms
            retryJitter:  200 // time in ms
        }
    );
    const resource = 'locks:account:322456';
    const ttl = 3000;
    async function getData(key){
        let result = await redis.get(key);
        if(!result){
            //获得锁
            try {
                const lock = await redlock.lock(resource, ttl);
                if(lock){
                    const dbData = await getDataFromDb(key);
                    if(dbData){
                        await redis.set(key,JSON.stringify(dbData),'PX',300000,'NX');
                    }
                    //释放锁
                    await lock.unlock();
                }else{
                    // 等待一会继续
                    await new Promise(resolve => setTimeout(resolve,3000));
                    result =  await getData(key);
                }
            } catch (error) {
                
            }
           
        }
        return result;
    }
    // 数据库查询
    async function getDataFromDb(key){
    
    }

     参考:

    https://juejin.im/post/5dbef8306fb9a0203f6fa3e2#heading-9

    https://www.csdn.net/gather_2e/MtTaQgysNTg1OTItYmxvZwO0O0OO0O0O.html

  • 相关阅读:
    【MM系列】SAP库龄报表逻辑理解
    【MM系列】SAP技巧之更改布局
    【MM系列】SAP里批量设置采购信息记录删除标记
    《跃迁-从技术到管理的硅谷路径》读书笔记
    Java安全编码标准
    web安全/渗透测试--1--web安全原则
    使用spring validation完成数据后端校验
    9 个Java 异常处理的规则
    程序员必看:给你一份详细的Spring Boot知识清单
    Java架构技术知识点梳理
  • 原文地址:https://www.cnblogs.com/xiaosongJiang/p/13034737.html
Copyright © 2020-2023  润新知