问题背景:当前需要实现一个硬件准入的功能,通过获取数据库中设备过期记录,判断设备是否能够正常使用
这里除了之前学到的
也需要用到缓存,对于缓存的异常一直弄混了,直到看到一种解释:
- 缓存雪崩:大量的key都没有了,请求像雪崩一样打到数据库上
- 缓存击穿:Redis这个盾被刺了一个洞(单个key没有了,热点Key),这个Key的请求像剑一样刺破盾刺向数据库
- 缓存穿透:Redis这个盾没有防住(这个Key缓存没有),这个Key的请求像剑一样穿过盾刺向数据库
缓存雪崩
当某一个时刻出现大规模的缓存失效的情况,那么就会导致大量的请求直接打在数据库上面,导致数据库压力巨大,那么可能的情况就是
- 大量的Key采用的相同的过期时间
- Redis宕机
解决方案:
- 解决大量Key同时过期,在原有的失效时间上加上一个随机值,比如1-5min避免相同过期时间导致雪崩
- 按照实际情况限制请求,使用限速机制保障请求不会大面积打到数据库上(熔断与降载不能应对突发情况)
- 提升数据库的容灾能力,通过分库分表、读写分离的策略
- 提升Redis集群,提高Redis的容灾能力
- 热点数据永不过期
缓存击穿
缓存雪崩是大规模的key失效,而缓存击穿是一个热点的Key,有大并发集中对其进行访问,突然间这个Key失效了,导致大并发全部打在数据库上,导致数据库压力剧增。这种现象就叫做缓存击穿
解决方案:
-
解决 redis 热点 Key 过期的问题,设置成不过期。
如何判断一个 Key 是否是热点,可以通过 Redis 的 MONITOR 命令实时监控 Redis 服务器接收到的命令,并将其输出到控制台或日志文件中。热点key一般是人为指定的(如果谁请求多谁就是热点key不过期也是有问题的),所以不需要实时性
-
使用互斥锁。如果缓存失效的情况,只有拿到锁才可以查询数据库,降低了在同一时刻打在数据库上的请求,防止数据库打死。
注意是缓存失效的情况下才去获取互斥锁
缓存穿透
发送的请求传进来的key是不存在,导致大量请求直接打到了数据库上
解决方案:
- 解决Key不在缓存的问题,把无效的Key也存到缓存中,缓存空值
- 如果Key每次都不一样,是不是就失效了。这样就需要用到布隆过滤器
- 大量的Key都存储在缓存中,可能造成缓存污染,需要设置合理的缓存时间
- 解决Key不存在的判断问题,将数据通过布隆过滤器缓起来,在查缓存之前查询布隆过滤器,如果Key不存在,某个 key 不存在,那么就一定不存在。如果某个Key存在,那么有可能存在
- 适用于数据量小的问题,如果数据量大,那么内存中会有大量的缓存数据
- 如果使用Redis中的BitMap呢,那么每次都需要查询两遍Redis
- 注意数据更新的情况
- 如果数据删除,就需要重新构建布隆过滤器
- 需要实时同步到布隆过滤器,否则可能会误杀
- 适用于数据量小的问题,如果数据量大,那么内存中会有大量的缓存数据
- 共享调用
关联缓存场景
- 缓存预热:在系统上线前,提前将数据加载到缓存中
- Redis及以上版本可以通过
redis-cli --latency-history
来查看 Redis 服务器的实时响应时间。在这个命令的输出结果中,可以看到每秒钟 Redis 接收到的请求数量,以及每个请求的响应时间。通过监控这些指标
缓存淘汰
Redis共支持八种淘汰策略,分别是noeviction、volatile-random、volatile-ttl、volatile-lru、volatile-lfu、allkeys-lru、allkeys-random 和 allkeys-lfu 策略。
怎么理解呢?主要看分三类看:
- 不淘汰
- noeviction(v4.0后默认的):一旦缓存写满,不再为写请求提供服务
- 对设置了过期时间的数据中进行淘汰
- 随机:volatile-random,在设置了过期时间的键值对中,随机删除
- ttl:volatile-ttl,越早过期的数据越优先选择
- lru:volatile-lru,最近最少使用的原则
- lfu:volatile-lfu,先根据最近最少使用淘汰,如果访问次数相同,再根据访问时间更久的数据进行淘汰
- 全部数据进行淘汰
- 随机:allkeys-random
- lru:allkeys-lru
- lfu:allkeys-lfu