Redis缓存

目录

一、引言

二、什么是缓存

三、缓存的更新策略

3.1 定期更新

3.2 实时更新

四、Redis缓存的工作原理

4.1 工作流程

4.2 缓存过期策略

4.3 内存淘汰策略

五、缓存常见问题及解决方案

5.1 缓存预热

5.2 缓存穿透

5.3 缓存击穿

5.4 缓存雪崩

5.5 缓存与数据库一致性

六、结语



一、引言

缓存是应对高并发、提升系统性能的关键技术,而 Redis 凭借其卓越性能,成为缓存场景中的核心选择。本文将从缓存的基本概念入手,理解 Redis 缓存的工作原理,同时拆解实际应用中常见的缓存问题及应对思路。

二、什么是缓存

我们可以回想坐高铁的经历:从进站检票、过安检,到最后出站,前前后后要刷好几次身份证。这时候,没人会把身份证塞进行李箱里 —— 虽说箱子空间大,但每次拿取都要翻找,特别麻烦。大家更习惯把它放在口袋或随手能摸到的地方,方便拿。这里的 “口袋”,其实就相当于行李箱的 “缓存”。

所以,什么是缓存呢?事实上,缓存是相对来讲的。比如在计算机中,CPU 从硬盘读取数据其实很慢。要是每次用到数据都去访问硬盘的话,CPU 将要花很大一部分时间等数据传过来,根本就没法充分发挥它的处理能力。为了解决这个问题,内存就派上了用场。CPU 访问内存的速度比访问硬盘快得多,把平时常用的数据从硬盘加载到内存里,CPU 要用时直接从内存拿,这样系统处理数据的效率一下子就提上来了。所以,内存就是硬盘的缓存。

Redis是基于内存的,相比于MySQL等需要访问磁盘的数据库来说快不少,所以Redis就可以做为MySQL的缓存。

总的来说,高速介质可以作为低速介质的缓存。

三、缓存的更新策略

缓存更新的策略可以分为两大类,定期更新和实时更新。

3.1 定期更新

每隔⼀定的周期, 基于日志对访问的数据频次进行统计,挑选出访问频次最高的前 N% 的数据,放入缓存。

3.2 实时更新

对于每一次访问,如果Redis缓存命中,则直接返回数据给用户。如果未命中,那么就访问数据库,然后将数据返回给用户,同时将这个数据写入Redis缓存。

四、Redis缓存的工作原理

4.1 工作流程

对于读请求:先查询Redis缓存。若缓存命中,则直接返回。如果缓存未命中,那么就将请求打到数据库,然后将数据库的查询结果写入缓存,并返回给客户端。

对于写请求:先更新数据库,然后在从缓存中删除相关数据块。

为什么要删除Redis缓存中的相关数据块?

为了确保数据一致。如果采取的是更新Redis相关数据块的策略,那么在更新的过程中,可能会有客户端获取这个数据,因为此时这个数据还存在于缓存中,所以不会访问数据库,这样就有可能还没完成数据的更新,这个旧数据就被客户端读走了,就导致了数据的不一致。直接删除,让请求直接打到数据库,然后在将这个数据写入缓存是更稳妥的做法,访问数据库虽然比缓存慢,但是它可以保证数据是最新的。

4.2 缓存过期策略

因为内存有限,所以不能只一味的往里写,还要考虑如何清理不需要的数据。

当我们往Redis中插入某个键,并且给这个键设置了过期时间。那么Redis 就会将该键放入一个过期字典中(类似哈希表结构),记录键与其过期时间的映射关系。

Redis的过期策略主要有两种:

1)惰性删除。只有当访问某个键时,才检查它的过期时间,如果过期,就删除。因为不用频繁的去检测这些键的过期时间,所以CPU开销就小,这是它的优点。但是,可能会导致大量的过期键堆积,浪费内存,这是它的缺点。

2)定期删除。每隔一段时间,随机抽取部分过期键检查并删除。如果时间间隔比较小,那么内存中过期的键将会得到及时的清除。但是,太频繁的检查也会导致CPU开销大。

Redis则是将以上两种方式混合,平衡CPU消耗和内存占用。

4.3 内存淘汰策略

内存淘汰策略,是Redis进行内存管理的一个兜底策略。只有内存达到 maxmemory 阈值时才会触发。触发内存淘汰策略,说明过期键都删完了,内存还是满的,这时候,只能对还未过期的键下手了。Redis内置的淘汰策略如下:

策略说明
volatile-lru从设置了过期时间的键中,删除 “最近最少使用”(LRU)的键。
allkeys-lru从所有键中,删除 “最近最少使用” 的键(适用于缓存场景,所有键都是缓存)。
volatile-random从设置了过期时间的键中,随机删除。
allkeys-random从所有键中,随机删除。
volatile-ttl从设置了过期时间的键中,删除 “剩余过期时间最短” 的键。
noeviction不删除任何键,直接返回错误(默认,不推荐缓存场景)。

五、缓存常见问题及解决方案

5.1 缓存预热

缓存预热简单说,就是在高访问量来临前,主动把热点数据加载到缓存中的操作,避免用户请求直接冲击数据库。这一份热点数据可以那么准确,只要能帮助数据库抵挡大部分请求即可,然后随着程序的运行,原来缓存的热点数据会被自动调整,适应当前情况。

5.2 缓存穿透

缓存穿透(Cache Penetration)和下面要讲的缓存击穿(Cache Breakdown)很容易混淆。

客户端请求的数据,在缓存(Cache)中不存在,在数据库(DB)中也不存在。这就叫做缓存穿透。

导致缓存穿透的原因有以下几种:

1)业务设计不合理,缺少必要的参数校验环节,对非法的key也进行查询。

2)误删了数据库中的部分数据。

3)黑客攻击。

比如,客户端发起请求,查询一个不存在的 ID(如用户 ID=-1)。缓存中未命中该数据。请求穿透到数据库,数据库查询后发现也没有该数据。黑客就可以对大量不存在的ID进行请求,然后这些请求全被打到数据库,就会导致数据压力过大,甚至扛不住。

对应的解决方案就是:对于不存在的key,将它的value设为空,然后写入到Redis缓存中,并且设置合理的过期时间,当再次访问这个不存在的key时,直接缓存命中返回空。

还有另一种解决方案:布隆过滤器。

在缓存前面,加上一层布隆过滤器。把数据库中所有的key都存入布隆过滤器中,然后客户端的请求向到布隆过滤器这一层,判断对应的key是否存在,不存在就直接返回。如果存在,就继续向下访问。

虽然说布隆过滤器存在一定的误判率,但是对于不存在的数据,它的判断是准确的。这个误判率的意思就是,原本就不存在的数据,被布隆过滤器误判为存在。不过问题不大,误判之后该请求直接打到数据库,此时数据库在二次判断即可。

5.3 缓存击穿

客户端请求的数据,在缓存中不存在,但在数据库中存在,通常是因为缓存刚好过期失效。这个就叫做缓存击穿。

所以,缓存穿透和缓存击穿的区别在哪?

缓存穿透请求的是原本就不存在的数据,而缓存击穿是请求存在的数据,但是它缓存失效了,需要到数据库读取该数据。

比如,某热门数据(如热门商品详情)的缓存突然过期。大量用户同时请求该数据,缓存全部未命中。所有请求同时打到数据库,查询同一数据。数据库返回数据后,将数据重新写入缓存,后续请求从缓存获取。这就是一个典型的缓存击穿。

解决方案有以下几种:

1)使用互斥锁。当缓存未命中时,只能有一个请求获取互斥锁进入数据库查询,其他请求等待,然后查询结果写入缓存后,等待的请求从缓存中获取数据,避免并发查库。

2)热点数据永不过期,不对热点数据设置过期时间。

3)同一类热点数据,将它们的过期时间错开(可以是一个基础时间加上随机时间),避免集中过期。

5.4 缓存雪崩

大量不同 Key的缓存同时失效,或者缓存服务直接宕机,导致海量不同类型的请求同时穿透到数据库。

这里注意和缓存击穿区分开来,缓存击穿是同一类key集中过期,而缓存雪崩是不同类型的key缓存失效。

解决方案:

1)所有的key要错开过期时间,避免同一时间大量不同类型的key过期。

2)搭建缓存服务集群(如 Redis Cluster),避免单点故障导致整个缓存不可用。

3)服务降级,当请求量过高时,直接拒绝部分请求,保护数据库。

5.5 缓存与数据库一致性

对于写请求:先更新数据库,然后在从缓存中删除相关数据块。具体原因已经在4.1节中详细解释。

六、结语

“路虽远,行则将至。事虽难,作则可成。”


完~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值