缓存雪崩就是在同一段时间内有大量缓存数据失效了(缓存过期了、被内存淘汰策略淘汰了或者 Redis 服务宕机了),然后在这段时间内又有请求想要获取这些数据,因为这些缓存数据已经不在 Redis 中了,所以最终就导致全部请求到了数据库(数据库压力大)。
解决方案:
- 将数据分类,不同的分类过期时间不同,或者使用随机过期时间
- 超热数据使用永久 key
- 对要过期的数据做访问量分析,判断是不是要延长过期时间
- redis 集群
- 降流、限流
- 加锁
缓存击穿
缓存击穿它是热点 key 问题;就是说有一个缓存失效了,但是同一段时间又有一大堆请求想访问这个数据,最终也会导致数据库压力过大。
解决方案:
- 预先将某条缓存设置为热点,增加缓存的过期时间
- 根据访问量动态调整缓存过期时间
- 使用二级缓存,单独保存这些热点数据
- 加锁
缓存击穿和缓存雪崩很像,雪崩是一大堆缓存失效了,击穿是几个热点缓存失效了。
缓存雪崩和缓存击穿问题,都是在高并发情况下出现的;缓存失效了,同一时间段又有很多请求访问这些数据,没有在 Redis 中命中,最终导致都会请求到数据库。
缓存穿透
缓存穿透就是访问了数据库中没有的数据,数据库查询不出来数据,就没有办法建立缓存,所以还是会一致请求数据库。
解决方案:
- 缓存空对象:当数据库中也不存在数据的时候,在 Redis 中缓存一个空值;但是这样会造成额外的内存消耗和短期的数据不一致
- 布隆过滤:在客户端和 Redis 之间增加了布隆过滤器;当布隆过滤中没有数据的时候,就会直接禁止流程继续执行(禁止访问 Redis 和数据库)
- 实时监控 key 的命中率:不同的时间段,超过一定阈值就排查问题
- key 加密:将请求的数据加密,每天更换加密字符串
布隆过滤器禁止流程继续执行,说明数据 100% 不存在;但是允许流程继续执行并不是 100% 准确的,所以还是有一定的穿透风险。
缓存预热
缓存预热就是防止服务器启动后立马宕机了。
可以在服务启动前,将一些热点数据(日常统计)先添加到 Redis 中,
缓存更新策略
我们将数据保存在缓存和数据库中,当修改了数据库中的数据后,就需要用到缓存更新策略对缓存进行更新;缓存更新策略就是解决缓存一致性问题的。
内存淘汰 | 超时剔除 | 主动更新 | |
---|---|---|---|
说明 | 不用自己维护,利用 Redis 的内存淘汰机制,当 内存不足时 自动淘汰部分数据,然后等到下次查询时候会更新缓存。 | 给缓存数据 添加 TTL 时间,到期后自动删除缓存,等到下次查询时候会更新缓存。 | 这个是自己编写业务逻辑,在修改数据库的同时,还要更新缓存。 |
一致性 | 差 因为不确定哪些数据会被淘汰、什么时候被淘汰。 |
一般 因为更新频率是根据超时时间来的;假设将数据刚写到缓存,数据库就更新了,这会等到超时后才获取数据。 |
好 但是这没法保证完全一致,因为有可能程序崩溃。 |
维护成本 | 无 Redis 自动维护 |
低 在添加缓存的时候,顺便添加超时时间。 |
高 需要对业务进行编码 |
业务场景:
- 低一致性需求:使用内存淘汰机制。
- 高一致性需求:主动更新 + 超时剔除,超时剔除是为了保证程序突然崩溃后,在一段时间后还能更新缓存。
主动更新
主动更新目前有 3 中:
- 由缓存的调用者,在更新数据库的同时更新缓存。
- 缓存与数据库整合为一个服务,由服务来维护一致性。调用者调用该服务,无需关系缓存一致性问题。
- 调用者只操作缓存,让其它线程异步将缓存数据持久化到数据库,最终保持一致。
2、3 维护成本太高,最终还是选择 1。
网上虽然有文章写过一些更新策略,但是我觉得这些策略不需要关注,因为最终还是看业务需求,如果你的业务要求缓存和数据库必须一致,那么每次更新完数据库后必须更新 Redis 的缓存,否则可以等到缓存超时后更新。
key 过期删除策略和内存淘汰策略
key 过期删除策略:
- 定时删除:给 key 设置过期时间的时候会创建一个关联的定时器,当时间到达时,由定时器自动执行 key 的删除操作;这样做的好处是过期的 key 可以被立即删除,但是会消耗 CPU 降低吞吐量。
- 惰性删除:访问 key 的时候判断是不是过期,过期就删除 key;这样做的好处是不会占用 CPU 资源,但是会导致过期后 key 还在缓存中。
- 定期删除:每隔一段时间,随机判断一些 key 是不是过期了,过期了就删除;这样做的好处是减少了 CPU 资源的占用,同时能够清理掉过期的 key,但是执行时常和频率无法确定,和算法又关系。
Redis 选择惰性删除 + 定期删除这两种策略配和使用,以求在合理使用 CPU 时间和避免内存浪费之间取得平衡。
在配置文件 redis.conf 中,可以通过参数
hz <每秒运行多少次>
来设定定期删除时间。
内存淘汰策略
在配置文件 redis.conf 中,可以通过参数 maxmemory <bytes>
来设定最大运行内存,只有在 Redis 的运行内存达到了我们设置的最大运行内存,才会触发内存淘汰策略。 不同位数的操作系统,maxmemory 的默认值是不同的:
- 在 64 位操作系统中,maxmemory 的默认值是 0,表示没有内存大小限制,那么不管用户存放多少数据到 Redis 中,Redis 也不会对可用内存进行检查,直到 Redis 实例因内存不足而崩溃也无作为。
- 在 32 位操作系统中,maxmemory 的默认值是 3G,因为 32 位的机器最大只支持 4GB 的内存,而系统本身就需要一定的内存资源来支持运行,所以 32 位操作系统限制最大 3 GB 的可用内存是非常合理的,这样可以避免因为内存不足而导致 Redis 实例崩溃。
在配置文件 redis.conf 中,可以通过参数 maxmemory-policy
来设定内存淘汰策略:
- volatile-random:随机淘汰一些设置了超时时间的 key
- volatile-ttl:优先淘汰那些即将过期的 key
- volatile-lru:淘汰设置了过期时间,但偶尔使用的 key
- volatile-lfu:淘汰设置了过期时间,但已经不怎么用了的 key
- allkeys-random:随机淘汰一些 key
- allkeys-lru:淘汰偶尔使用的 key
- allkeys-lfu:淘汰已经不怎么使用的 key
- noeviction:关闭内存淘汰策略,如果关闭会抛出内存溢出
标签:缓存,过期,Redis,问题,雪崩,key,淘汰,内存 From: https://www.cnblogs.com/superylg/p/16755467.htmlLRU(Least Recently Used):最少使用的,可以理解为这个 key 偶尔被使用,LFU(Least Frequently Used):最不常用的,可以理解为这个 key 已经不怎么用了,random:随机选择一些 key,ttl:优先淘汰那些即将过期的 key。
volatile:只会淘汰设置了过期时间的 key,allkeys:会淘汰所有的 key。
如果使用了 volatile-lru、volatile-lfu、volatile-random 和 volatile-ttl 策略,当没有匹配到任何 key 的时候,它们不会移除任何 key 类似于 noeviction。