Redis缓存详解:内存淘汰和缓存的预热、击穿、雪崩、穿透的原理与策略
目录
缓存的概念
使用redis作为缓存
缓存更新策略
缓存淘汰策略
关于缓存常见问题及解决方案
缓存预热(Cache preheating)
缓存穿透(Cache penetration)
缓存雪崩(Cache avalanche)
缓存击穿(Cache breakdown)
缓存的概念
缓存和磁盘功能相似,同样是具有存储数据,写入数据,获取数据的特点。缓存相较于磁盘来说,传统缓存不具备持久化保存数据(redis提供持久化的功能),容量也远远小于磁盘的容量,但是却又着访问速度极快的特点。
例子:
行李箱和身上衣服的口袋分别可以看作磁盘和缓存,行李箱相较于口袋能够装下很多东西,但是从行李箱中拿东西确比较慢,而衣服口袋容量虽然很小,但是能很快把里面的东西拿出来。
使用redis作为缓存
通常在网站开发中我们会使用关系型数据库(例如MySQL)来存储数据,这种数据库虽然功能强大但有一个很大的缺陷,就是性能不高,一次查询消耗资源比较多,在面对高并发场景对于数据库的压力是很大的, 很容易就会使数据库服务器宕机。
解决数据库承担高并发的思路通常有两种:
开源:
①提高单台数据库服务器的硬件配置,增强处理能力。
②引入更多机器设备,读写分离,分库分表,构建数据库机器,分散压力。
节流:
引入缓存,在缓存中保存访问频繁的热点数据,降低直接对数据量访问的请求量(“二八定律”,20%的热点数据能满足80%的访问需求)。
Redis 就是⼀个用来作为数据库缓存的常见方案
客户端访问业务服务器, 发起查询请求。
业务服务器先查询 Redis, 看想要的数据是否在 Redis 中存在。如果已经在 Redis 中存在了,就直接返回,此时不必访问 MySQL 了。如果在 Redis 中不存在,再查询 MySQ。
注意:
缓存是用来加快 "读操作" 的速度的,如果是 "写操作",还是要老老实实写数据库,缓存并不能提高性能。
缓存更新策略
定期生成
每隔一段时间(根据实际需求进行配置),对于访问的数据频次进行统计,挑选出前N%的数据更新到缓存中。适用于一些对实时性要求不高,但访问频繁的数据
实时生成
设置好缓存的容量上限,在接收用户请求的过程中,存在缓存的数据就直接返回给用户,如果不存在则去数据库中查,再将结果写到redis缓存中。
当缓存中的容量已达上限,就需要根据不同的策略对旧数据进行淘汰删除。
缓存淘汰策略
FIFO (First In First Out) 淘汰最先进来的
把缓存中存在时间最久的 (也就是最先来的数据) 淘汰掉。
LRU (Least Recently Used) 淘汰最久未使用的
记录每个 key 的最近访问时间,把最近访问时间最老的 key 淘汰掉。
LFU (Least Frequently Used) 淘汰访问次数最少的
记录每个 key 最近⼀段时间的访问次数,把访问次数最少的淘汰掉。
Random 随机淘汰
从所有的 key 中抽取幸运儿被随机淘汰掉。
Redis 通过配置 maxmemory-policy
来决定淘汰策略:
volatile-lru:淘汰设置了过期时间的 key 中最久未使用的
allkeys-lru:淘汰最久未使用的 key
volatile-lfu:淘汰设置了过期时间的 key 中访问次数最少的
allkeys-lfu:淘汰访问次数最少的 key
volatile-random:随机淘汰设置了过期时间的 key
allkeys-random:随机淘汰 key
volatile-ttl:淘汰设置了过期时间,即将过期的 key
noeviction:超出内存后,写操作报错(默认策略)
关于缓存常见问题及解决方案
缓存预热(Cache preheating)
缓存预热指的是,在服务上线/重启或热点产生前,主动把数据写入缓存,避免上线瞬间大量穿透 mysql。
成因:
对于定期生成的情况不涉及预热,主要是针对实时生成下需要进行缓存预热。在redis服务器首次接入后,服务器里还没有数据,此时如果接收了客户端发来的大量请求,缓存命中失败就会直接打给mysql。
解决方式:
把定期生成和实时生成结合一下,先通过离线的方式,统计热点数据,将热点数据导入redis中。
缓存穿透(Cache penetration)
缓存穿透指的是,在查询某个key时,redis中没有,而mysql中也没有,这个key也不会被更新到redis中,如果这样的请求很多也会给mysql带来很大压力。
成因:
-
业务涉及不合理,可能缺少必要的参数检验环节,导致非法的key也被进行查询了。
-
操作失误,将数据库部分数据勿删了。
-
黑客恶意攻击。
解决方式:
-
当发现这个key在redis和mysql上都不存在时,任然写入redis,将value值设为一个非法值(例如“”)。
-
引入布隆过滤器,将合法的key预装到布隆过滤器中,请求先询问布隆过滤器,不存在就直接返回,不去访问mysql。
缓存雪崩(Cache avalanche)
缓存雪崩指的是,由于在短时间内,redis上大规模的key失效,导致缓存命中率直线下降,打到mysql的请求激增压力迅速上升,引发mysql宕机。
成因:
-
redis 上的大量的 key同时过期。
-
redis 节点重启或挂了
-
缓存被清空或缓存容量不足导致大规模淘汰
解决方式:
-
设置key的过期时间随机化,避免同一时刻过期。
-
加强监控报警,提高redis集群的可用性。
缓存击穿(Cache breakdown)
缓存击穿指的是,某个热点的 key 在过期瞬间产生大量请求去 mysql,短时间内对该单key的mysql访问量激增。与雪崩区别,雪崩是大量 key 同时过期,而击穿是单 key(或少数几个热 key)的问题。
成因:
某个热点key设置了过期时间,过期后仍然有大量访问。
解决方式:
-
将热点数据设置为永不过期。
-
进行必要的降级服务,访问数据库的的时候使用分布式锁,限制同时请求数据库的并发数。