Redis缓存机制详解
Redis 作为一个高效的内存数据库,常用于缓存系统。
其缓存机制有助于提高数据访问速度、减轻后端数据库压力。
由于 Redis 是基于内存的数据库,内存资源有限,因此需要有合理的数据淘汰策略以管理内存使用。
1. 内存数据淘汰策略
在 Redis 配置文件中(通常是 redis.conf
),可以通过 maxmemory-policy
配置来设定具体的淘汰策略。
常见的策略包括:
volatile-lru
: 从设置了过期时间的数据集中,移除最近最少使用的数据。allkeys-lru
: 从所有数据集中,移除最近最少使用的数据。volatile-lfu
: 从设置了过期时间的数据集中,移除最不常使用的数据。allkeys-lfu
: 从所有数据集中,移除最不常使用的数据。volatile-random
: 从设置了过期时间的数据集中,随机移除数据。allkeys-random
: 从所有数据集中,随机移除数据。volatile-ttl
: 从设置了过期时间的数据集中,移除即将过期的数据。noeviction
: 不移除任何数据,当内存满时,返回错误。该策略通常不建议使用,因为它可能导致无法写入新数据。
2. 数据过期机制
Redis 支持对键设置生存时间(TTL)。当键的生存时间到达之后,Redis 会自动删除该键。
- EXPIRE key seconds: 设置键在指定秒数后过期。
- PEXPIRE key milliseconds: 设置键在指定毫秒数后过期。
- EXPIREAT key timestamp: 设置键在指定的时间戳 (单位为秒) 后过期。
- PEXPIREAT key milliseconds-timestamp: 设置键在指定的时间戳 (单位为毫秒) 后过期。
TTL key
和 PTTL key
命令检查键的剩余生存时间。具体命令细节可查看Redis通用命令详解。
3. LRU (Least Recently Used) 缓存实现
LRU(Least Recently Used,最近最少使用)是一种常见的缓存淘汰策略,用于在缓存空间不足时确定哪些数据应该被移除,以便为新数据腾出空间。
传统的 LRU 算法要求在每次访问数据时,都更新数据的访问时间,然后在淘汰时选择最久未被访问的数据。
但是Redis 的 LRU 算法并不是严格意义上的 LRU 算法,而是近似的 LRU 算法。
- Redis 会定期随机地从数据集中(默认是每秒钟从部分数据集中选择5个键),选择一些键检查是否过期或者需要淘汰。
- 这种方法相对于每次访问时都更新访问时间,大大减少了对每个数据访问的性能开销。
尽管 LRU 的实现略有不同,但它的核心思想与一般的 LRU 策略是相似的。
配置
-
maxmemory-policy: 在 Redis 配置文件中设置此参数,可以选择不同的淘汰策略,包括
volatile-lru
(对设置了过期时间的数据使用 LRU 策略)、allkeys-lru
(对所有数据使用 LRU 策略)等。 -
maxmemory-samples: 这个参数可以设置每次淘汰时从数据集中随机选择的键的个数,默认是 5。可以根据实际情况调整以影响 LRU 的淘汰效果。
4. LFU (Least Frequently Used) 缓存实现
LFU(Least Frequently Used,最不常用),与LRU不同的是,LFU策略基于数据的访问频率进行淘汰,而不是最近访问时间。
- 高效利用缓存:LFU特别适合那些访问频率有明显差异的数据集,例如一些热点数据被频繁访问,而其他数据较少访问的场景。
- 防止缓存污染:LFU可以有效防止缓存被短时间内大量访问但长期不再使用的数据占据。
和传统的LFU算法一样,Redis会优先淘汰那些访问频率较低的数据。
LFU策略的核心机制
-
访问频率计数器:
- 每个键都有一个访问频率计数器,用于记录该键被访问的次数。
- 这个计数器并不是简单的增加,而是经过某种衰减处理,以防止频繁访问的键长期占据缓存。
-
衰减机制:
- 为了防止计数器无限增长,Redis采用了一种衰减机制,使得访问频率会随着时间的推移而减小。
- 具体的衰减算法结合了访问时间和访问频率,确保那些历史上曾经被频繁访问但最近很少使用的键也会被适当淘汰。
-
近似实现:
- 类似于Redis的LRU实现,LFU策略也采用了一种近似方法来减少性能开销。
- Redis 并不会在每次访问时精确地更新所有键的访问频率,而是通过采样和概率进行近似估算。
配置
- maxmemory-policy: 在Redis配置文件中,
maxmemory-policy
参数可以设置为allkeys-lfu
或volatile-lfu
:allkeys-lfu
: 对所有键使用LFU策略。volatile-lfu
: 仅对设置了过期时间的键使用LFU策略。
- lfu-log-factor: 这个参数控制访问频率计数器的衰减速率。默认值是10,表示每次访问频率以log(10)的速度增长。值越大,计数器增长越慢,从而更强调近期访问频率。
- lfu-decay-time: 这个参数控制频率计数器的衰减时间。默认值是1小时,表示每小时衰减一次。更短的衰减时间意味着频率计数器更快地减小,从而使得缓存更容易淘汰那些不再频繁访问的键。
LFU 淘汰过程
- 访问频率记录:每次访问一个键时,对应的访问频率计数器会根据
lfu-log-factor
进行更新。 - 定期衰减:根据
lfu-decay-time
,Redis会定期对所有键的访问频率计数器进行衰减。 - 选择淘汰键:当需要淘汰键时,Redis会选择那些访问频率最低的键进行淘汰。这些键的选择也可能通过随机采样来完成,以减少性能开销。
5. 缓存穿透、缓存击穿和缓存雪崩
-
缓存穿透:
- 通常指缓存和数据库中都没有的数据,但大量请求却涌入,直接打到数据库上,导致数据库压力过大。
- 用户请求的数据本身不存在(例如恶意攻击请求大量不存在的数据)。
- 每次请求都会绕过缓存直接查询数据库,缓存不起作用。
常见解决办法:
-
缓存空对象
-
使用布隆过滤器
-
缓存击穿:
- 指的是指缓存中某个热点数据失效(过期)后,瞬时大量请求直接落到数据库,导致数据库压力骤增的现象。
- 热点数据在缓存中存在,并且有许多请求正在访问该数据。
- 缓存数据过期了,而这时有大量请求同时到来,所有请求都绕过缓存直接访问数据库。
常见解决方案:
- 缓存雪崩:
- 指的是缓存服务器(Redis)集群宕机或者大量缓存集中失效,导致大量请求直接打到数据库上,可能会压垮数据库。
- 同一时刻大量缓存数据同时过期。
- 缓存服务器宕机或重启,导致缓存不可用。
常见解决方案:
Tips:了解更多Redis知识,可以去博主的专栏查看哦~