redis的内存
在聊我们的key的过期策略之前,我们先来看看redis的默认内存是多少,这个不用多说肯定是在redis的配置文件中的,而redis的配置文件中maxmemory <字节>是以字节为单位的,而且我们的配置文件中初始的大小就是0,这个配置在32位系统中,最大会吃3G的系统内存,如果是64位的系统就会吃的干干净净,一般在机器上设置的redis的内存就是机器内存的3/4也就是75%。
那么怎么修改内存大小呢?第一个就是去配置文件中修改,然后重启redis服务器,第二种就是使用 comfig set maxmemory <字节大小> 进行修改。
那怎么查看呢?和上面差不多,要么去配置文件看,要么是有comfig get maxmemory命令查看,或者是有info memory 进行查看。
那如果内存已经满了,再进行set的话会怎么样呢?其实和java一样会报OOM错误,提示内存不足了。
内存过期策略
为了解决OOM的错误,redis有对应的内存过期策略,就是为例防止可以过多,或者key永不过期导致的内存溢出的问题,有以下三种策略:需要注意的是这里的策略都是主动的,也就是在内存没有满的时候进行删除的。
分别是直接删除:也就是立刻马上,不管redis有多忙压力有多大,也要在执行完当前命令后,执行这个命令,当然需要排队的,只有轮到这个命令了才会立刻执行,因为主线程就是一个单线程的。第二种就是惰性删除:也就是到了过期时间不会主动去删除这个key,只有当再一次访问这个key的时候,检查到是过期了,返回客户端不存在,然后删除这个key,而删除这个key默认情况下也是异步删除的,因为**lazyfree-lazy-server-del**这个命令,我们第九篇有说过。
第三种就是定期删除策略,这个就是前两个的折中方案,因为第一种是对空间友好但是对时间不友好(CPU立刻删除Bigkey可能服务卡顿),第二种就是时间利好对空间不好,因为可能有些数据早过期了一直没有被再次访问导致辣椒数据堆积浪费内存。
而定期策略就是在控制每秒检查的频率,检查的强度。
每次检查的key数量 :20个(这是Redis源码中的硬编码常量)
过期比例阈值 :25%(如果超过25%的key过期,就继续检查)
最大执行时间 :25ms(每次最多执行25ms,避免阻塞)
每次定期删除执行时:
1. 从设置了过期时间的key中随机抽取N个key
2. 检查每个key是否过期
3. 删除所有过期的key
4. 计算过期key的比例
# 设置定期删除频率(每秒执行次数)
redis-cli CONFIG SET hz 20
# 设置定期删除强度(Redis 7.0+)
redis-cli CONFIG SET active-expire-effort 5
第二种
# 编辑 /etc/redis/redis.conf
sudo vim /etc/redis/redis.conf
# 添加或修改这两行:
hz 20 # 控制频率
active-expire-effort 5 # 控制强度(Redis 7.0+)
# 重启Redis生效
sudo systemctl restart redis
这个强度就是控制每次检查key的数量,最大是10,最小是1,当设置为10的时候,每次检查的key就是200个,最大的时间就是250ms。
内存淘汰策略
我们的内存淘汰策略相对于内存过期策略来说,他是一个被动的,也就是只有当redis内存不足准备OOM的时候才会触发的,而过期策略是在没满的时候就在触发或者叫随时准备触发。而这种淘汰策略一共有八种,分为五大类。
| 类别 | 策略名称 | 中文名 | 淘汰范围 | 淘汰规则 |
|---|---|---|---|---|
| 不淘汰 | noeviction |
不淘汰 | - | 不淘汰,内存满时报错 |
| LRU类 | allkeys-lru |
全局LRU | 所有key | 最近最少使用 |
volatile-lru |
过期key LRU | 有过期时间的key | 最近最少使用 | |
| LFU类 | allkeys-lfu |
全局LFU | 所有key | 最不经常使用 |
volatile-lfu |
过期key LFU | 有过期时间的key | 最不经常使用 | |
| 随机类 | allkeys-random |
全局随机 | 所有key | 随机淘汰 |
volatile-random |
过期key随机 | 有过期时间的key | 随机淘汰 | |
| TTL类 | volatile-ttl |
过期时间最近 | 有过期时间的key | 剩余生存时间最短 |
不淘汰类
这个很好理解,就是满了直接抛出OOM不会进行任何的操作,这个策略一般使用在数据十分重要的场景,就算满了也不能把数据丢了。
# 1. noeviction(不淘汰,内存满时报错)
redis-cli CONFIG SET maxmemory-policy noeviction
# 特点:
# - 内存满时,新写入操作返回错误
# - 读取操作正常
# - 不淘汰任何key
# 使用场景:
# 1. 数据绝对不能丢失的场景
# 2. 需要人工介入处理内存问题的场景
# 3. 有严格数据完整性要求的场景
# 返回错误示例:
# (error) OOM command not allowed when used memory > 'maxmemory'
LRU类(Least Recently Used最近最少使用)
首先我们来说说什么是LRU,简单来说就是淘汰最久没被访问的key,认为很久没被访问的数据,将来也不太可能被访问。
访问顺序:A → B → C → D → A → C
LRU链表变化:
初始: [空]
访问A: [A] # A最新
访问B: [B, A] # B最新,A次新
访问C: [C, B, A] # C最新
访问D: [D, C, B, A] # D最新
访问A: [A, D, C, B] # A被重新访问,提到最前面
访问C: [C, A, D, B] # C被重新访问,提到最前面
淘汰时:淘汰链表末尾的B(最久没被访问)
# Redis使用近似LRU算法(非精确)
# 原因:完全精确的LRU需要维护链表,内存开销大
# 实现:随机采样N个key,淘汰其中最久没被访问的
# 配置采样数量(默认5个)
maxmemory-samples 5
# 工作流程:
1. 内存满了,需要淘汰key
2. 随机抽取5个设置了过期时间的key(volatile-lru)
3. 从这5个中淘汰最久没被访问的
这个是LRU多工作流程,我们在设置采样数量,然后直接从这个被随机选择的几个中淘汰一个最久没使用的,LRU也就是内存满了后,插入一个key,就会有一个key被踢出群聊。
这个是两种LRU策略,分为allkeys-lru,volatile-lru。
# 2. allkeys-lru(全局LRU)
# 从所有key中淘汰最近最少使用的key
redis-cli CONFIG SET maxmemory-policy allkeys-lru
# 适用场景:纯缓存服务,所有key都可淘汰
# 例如:商品信息缓存、用户信息缓存
# 3. volatile-lru(有过期时间的LRU)
# 从设置了过期时间的key中淘汰最近最少使用的key
redis-cli CONFIG SET maxmemory-policy volatile-lru
# 适用场景:
# 1. 部分数据永久,部分数据有生命周期
# 2. Session存储
# 3. Token缓存
LFU类 (Least Frequently Used最不经常使用)
这个和LRU有点不同,LFU是淘汰访问频率最低的key,认为很少被访问的数据,将来也不太可能被访问。他和LRU的区别就是,LRU记录访问顺序淘汰末尾,和队列一样先进先出(指的是访问顺序),LFU记录的是访问频率,把频率最低的踢出去。
访问次数统计:
A: 访问10次
B: 访问5次
C: 访问100次
D: 访问2次
E: 访问1次
淘汰时:淘汰访问次数最少的E(访问1次)
# Redis的LFU = 访问频率 + 衰减时间
# 不是简单的访问计数,而是有衰减机制
# 每个key的LFU值 = 访问频率 + 衰减因子
# 访问频率会随时间衰减(防止旧数据永远不被淘汰)
# LFU配置参数:
# lfu-log-factor:计数器对数因子(1-10,默认10)如:10次增加1次
# 值越大,计数器增长越慢
lfu-log-factor 10
# lfu-decay-time:衰减时间(1-60,默认1,单位分钟)
# 多久没有访问,访问次数减1
lfu-decay-time 1
这个是LFU的工作原理,和LRU不同的是,这个是记录所有满足条件的kye的频率,然后当OOM时就会提出频率最低的一个,也是一样的进一个踢一个。
这个是LFU的两种策略
# 4. allkeys-lfu(全局LFU)
# 从所有key中淘汰最不经常使用的key
redis-cli CONFIG SET maxmemory-policy allkeys-lfu
# 适用场景:访问频率有明显差异的数据
# 例如:配置缓存、热点数据
# 5. volatile-lfu(有过期时间的LFU)
# 从设置了过期时间的key中淘汰最不经常使用的key
redis-cli CONFIG SET maxmemory-policy volatile-lfu
# 适用场景:
# 1. 有生命周期的热点数据
# 2. 广告位数据
核心区别
| 特性 | LRU | LFU | 说明 |
|---|---|---|---|
| 淘汰依据 | 访问时间 | 访问频率 | 核心差异 |
| 适用场景 | 热点数据明显 | 访问模式稳定 | 场景不同 |
| 实现复杂度 | 简单 | 复杂 | LFU更复杂 |
| 内存开销 | 小 | 较大 | LFU需要记录频率 |
| Redis版本 | 所有版本 | 4.0+ | 版本要求 |
| 处理冷数据 | 可能误保留 | 较好识别 | LFU更好 |
他们俩兄弟处理结果是完成不同的,如下:
访问模式:A(新) B(新) C(旧但频繁) D(旧且少用)
LRU结果:
当前状态: [A, B, C, D] # A最新,D最旧
访问C后: [C, A, B, D] # C变最新
内存满时淘汰: D # 淘汰最旧的D
LFU结果:
访问次数: A(5) B(3) C(50) D(1)
内存满时淘汰: D # 淘汰次数最少的D
随机类策略
这个就没有什么好说的了,就是随便找一个有猿人踢了。
# 6. allkeys-random(全局随机)
# 从所有key中随机淘汰
redis-cli CONFIG SET maxmemory-policy allkeys-random
# 适用场景:所有key价值相同
# 例如:临时数据、不重要缓存
# 7. volatile-random(有过期时间随机)
# 从设置了过期时间的key中随机淘汰
redis-cli CONFIG SET maxmemory-policy volatile-random
# 适用场景:
# 1. 有过期时间的、价值相同的数据
# 2. 测试环境
TTL类策略
这个也没什么好说的,就是从所有设置过期时间的key中,找到最快过期的踢了,也不差这一会了属于是哈哈。
# 8. volatile-ttl(剩余时间最短优先)
# 从设置了过期时间的key中淘汰剩余生存时间(TTL)最短的
redis-cli CONFIG SET maxmemory-policy volatile-ttl
# 适用场景:
# 1. 希望尽快清理即将过期的key
# 2. Token、验证码等短期数据
# 3. 会话数据
这个是八大策略的使用场景推荐。
| 场景 | 推荐策略 | 理由 | 额外配置 |
|---|---|---|---|
| 缓存系统 | allkeys-lru | 缓存都可淘汰,保留热点 | maxmemory-samples 10 |
| Session存储 | volatile-lru | session有过期时间 | 设置合适过期时间 |
| 配置缓存 | allkeys-lfu | 配置访问频率稳定 | lfu-log-factor 10 |
| Token存储 | volatile-ttl | token即将过期可提前淘汰 | 短过期时间 |
| 重要数据 | noeviction | 不能丢失数据 | 监控内存,及时扩容 |
| 消息队列 | allkeys-lru | 消息可丢弃,保留最新 | 大内存 |
| 实时排名 | volatile-lru | 排名数据有过期时间 | 结合持久化 |
总结
在redis中,内存的清除有几种行为,一种就是我们主动的删除(使用del命令),第二种就是过期的清理,第三种就是内存满了的被动踢人。这个我们得分清楚,第一种可以看看第9篇。