一、缓存雪崩
描述:
大量的key设置了相同的过期时间,导致在缓存在同一时刻全部失效,造成瞬时DB请求量大、压力骤增,引起雪崩。
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