redis的淘汰算法持久化

本文深入探讨Redis的过期淘汰策略,包括volatile-lru、allkeys-lru等,并解析Redis的RDB与AOF两种持久化方式的工作原理、配置及优化策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Redis的淘汰策略

如果你的 Redis 只能存8G数据,你写了11G,那么 Redis 会怎么淘汰那3G数据呢? redis.conf 中的过期淘汰配置 看下源码的配置

# MAXMEMORY POLICY: how Redis will select what to remove when maxmemory
# is reached. You can select among five behaviors:
#最大内存策略:当到达最大使用内存时,你可以在下面5种行为中选择,Redis如何选择淘汰数据库键

#当内存不足以容纳新写入数据时

# volatile-lru -> remove the key with an expire set using an LRU algorithm
# volatile-lru :在设置了过期时间的键空间中,移除最近最少使用的key。这种情况一般是把 redis 既当缓存,又做持久化存储的时候才用。

# allkeys-lru -> remove any key according to the LRU algorithm
# allkeys-lru : 移除最近最少使用的key (推荐)

# volatile-random -> remove a random key with an expire set
# volatile-random : 在设置了过期时间的键空间中,随机移除一个键,不推荐

# allkeys-random -> remove a random key, any key
# allkeys-random : 直接在键空间中随机移除一个键,弄啥叻

# volatile-ttl -> remove the key with the nearest expire time (minor TTL)
# volatile-ttl : 在设置了过期时间的键空间中,有更早过期时间的key优先移除 不推荐

# noeviction -> don't expire at all, just return an error on write operations
# noeviction : 不做过键处理,只返回一个写操作错误。 不推荐

# Note: with any of the above policies, Redis will return an error on write
#       operations, when there are no suitable keys for eviction.
# 上面所有的策略下,在没有合适的淘汰删除的键时,执行写操作时,Redis 会返回一个错误。下面是写入命令:
#       At the date of writing these commands are: set setnx setex append
#       incr decr rpush lpush rpushx lpushx linsert lset rpoplpush sadd
#       sinter sinterstore sunion sunionstore sdiff sdiffstore zadd zincrby
#       zunionstore zinterstore hset hsetnx hmset hincrby incrby decrby
#       getset mset msetnx exec sort

# 过期策略默认是:
# The default is:
# maxmemory-policy noeviction

复制代码
  • noeviction:当内存不足以容纳新写入数据时,新写入操作会报错。
  • allkeys-lru:当内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的key。
  • allkeys-random:当内存不足以容纳新写入数据时,在键空间中,随机移除某个key。
  • volatile-lru:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,移除最近最少使用的key。
  • volatile-random:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,随机移除某个key。
  • volatile-ttl:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,有更早过期时间的key优先移除。

其实我觉得用volatile-lru就好了 毕竟报错是完全没有必要的 还有就是设置一个报警装置 如果不够了 就搞主从 哈哈

Redis的持久化方式

Redis为持久化提供了两种方式:

  • RDB:在指定的时间间隔能对你的数据进行快照存储。
  • AOF:记录每次对服务器写的操作,当服务器重启的时候会重新执行这些命令来恢复原始的数据。

为了使用持久化的功能,我们需要先知道该如何开启持久化的功能。在redis.conf 中有下面的配置:

img

RDB的配置

# 时间策略
save 900 1
save 300 10
save 60 10000

# 文件名称
dbfilename dump.rdb

# 文件保存路径
dir /home/work/app/redis/data/

# 如果持久化出错,主进程是否停止写入
stop-writes-on-bgsave-error yes

# 是否压缩
rdbcompression yes

# 导入时是否检查
rdbchecksum yes
复制代码

save 900 1 表示900s内如果有1条是写入命令,就触发产生一次快照,可以理解为就进行一次备份 save 300 10 表示300s内有10条写入,就产生快照

下面的类似,那么为什么需要配置这么多条规则呢?因为Redis每个时段的读写请求肯定不是均衡的,为了平衡性能与数据安全,我们可以自由定制什么情况下触发备份。所以这里就是根据自身Redis写入情况来进行合理配置。

stop-writes-on-bgsave-error yes
这个配置也是非常重要的一项配置,这是当备份进程出错时,主进程就停止接受新的写入操作,是为了保护持久化的数据一致性问题。如果自己的业务有完善的监控系统,可以禁止此项配置, 否则请开启。

关于压缩的配置 rdbcompression yes ,建议没有必要开启,毕竟Redis本身就属于CPU密集型服务器,再开启压缩会带来更多的CPU消耗,相比硬盘成本,CPU更值钱。

当然如果你想要禁用RDB配置,也是非常容易的,只需要在save的最后一行写上:save “”, 默认是开启的。

RDB的原理

在Redis中RDB持久化的触发分为两种:自己手动触发与Redis定时触发。
针对RDB方式的持久化,手动触发可以使用:

- save:会阻塞当前Redis服务器,直到持久化完成,线上应该禁止使用。
- bgsave:该触发方式会fork一个子进程,由子进程负责持久化过程,因此阻塞只会发生在fork子进程的时候。
复制代码

而自动触发的场景主要是有以下几点:

- 根据我们的 save m n 配置规则自动触发;
- 从节点全量复制时,主节点发送rdb文件给从节点完成复制操作,主节点会触发 bgsave;
- 执行 shutdown时,如果没有开启aof,也会触发。
复制代码

由于 save 基本不会被使用到,我们重点看看 bgsave 这个命令是如何完成RDB的持久化的。

img

这里注意的是 fork 操作会阻塞,导致Redis读写性能下降。我们可以控制单个Redis实例的最大内存,来尽可能降低Redis在fork时的事件消耗。以及上面提到的自动触发的频率减少fork次数,或者使用手动触发,根据自己的机制来完成持久化。

保存已经会了,接下来我看看如何恢复数据-> 将备份文件复制到Redis的暗安装目录下,然后重新启动服务。

AOF的配置

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XHbDQrFH-1575880827073)(data:image/svg+xml;utf8,)]

# 是否开启aof
appendonly yes

# 文件名称
appendfilename "appendonly.aof"

# 同步方式
appendfsync everysec

# aof重写期间是否同步
no-appendfsync-on-rewrite no

# 重写触发配置
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb

# 加载aof时如果有错如何处理
aof-load-truncated yes

# 文件重写策略
aof-rewrite-incremental-fsync yes
复制代码

还是重点解释一些关键的配置:

appendfsync everysec 它其实有三种模式:

  • always:把每个写命令都立即同步到aof,很慢,但是很安全
  • everysec:每秒同步一次,是折中方案(默认也是这个)
  • no:redis不处理交给OS来处理,非常快,但是也最不安全

AOF的原理

AOF的整个流程大体来看可以分为两步,一步是命令的实时写入(如果是 appendfsync everysec 配置,会有1s损耗),第二步是对aof文件的重写。

对于增量追加到文件这一步主要的流程是:命令写入=》追加到aof_buf =》同步到aof磁盘。那么这里为什么要先写入buf在同步到磁盘呢?如果实时写入磁盘会带来非常高的磁盘IO,影响整体性能。

aof重写是为了减少aof文件的大小,可以手动或者自动触发,关于自动触发的规则请看上面配置部分。fork的操作也是发生在重写这一步,也是这里会对主进程产生阻塞。

手动触发: bgrewriteaof,自动触发 就是根据配置规则来触发,当然自动触发的整体时间还跟Redis的定时任务频率有关系。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EX35DPdk-1575880827074)(data:image/svg+xml;utf8,)]

数据的备份、持久化做完了,我们如何从这些持久化文件中恢复数据呢?如果一台服务器上有既有RDB文件,又有AOF文件,该加载谁呢?

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uy1nActF-1575880827075)(data:image/svg+xml;utf8,)]

启动时会先检查AOF文件是否存在,如果不存在就尝试加载RDB。那么为什么会优先加载AOF呢?因为AOF保存的数据更完整,通过上面的分析我们知道AOF基本上最多损失1s的数据。

Redis持久化的性能优化

通过上面的分析,我们都知道RDB的快照、AOF的重写都需要fork,这是一个重量级操作,会对Redis造成阻塞。因此为了不影响Redis主进程响应,我们需要尽可能降低阻塞。

- 降低fork的频率,比如可以手动来触发RDB生成快照、与AOF重写;
- 控制Redis最大使用内存,防止fork耗时过长;
- 使用更牛逼的硬件;
- 合理配置Linux的内存分配策略,避免因为物理内存不足导致fork失败。
复制代码

一些线上经验

- 如果Redis中的数据并不是特别敏感或者可以通过其它方式重写生成数据,可以关闭持久化,如果丢失数据可以通过其它途径补回;
- 自己制定策略定期检查Redis的情况,然后可以手动触发备份、重写数据;
- 可以加入主从机器,利用一台从机器进行备份处理,其它机器正常响应客户端的命令;
复制代码
<think>好的,我现在要回答用户关于Redis内存淘汰算法的问题。首先,我需要回忆一下Redis的内存淘汰机制有哪些策略,以及它们的实现方式。根据用户提供的引用内容,Redis主要有几种淘汰策略,比如LRU、LFU、Random和TTL等。 首先,用户想知道内存淘汰算法的介绍和实现方式。我需要先解释每个策略的基本概念,比如LRU是最近最少使用,LFU是最不经常使用,Random是随机删除,TTL是根据过期时间优先删除。然后需要结合引用中的信息,说明这些策略如何工作,比如当内存达到maxmemory时的处理流程。 接下来,我需要考虑实现方式。根据引用4和5,Redis的LRU和LFU可能不是传统的实现,而是采用了近似算法,比如抽样来减少性能消耗。例如,LRU可能在每个键中记录最近访问的时间戳,每次淘汰时随机选取一些键,找到最久未访问的那个。LFU可能记录访问频率,通过计数器来实现,并且可能定期衰减频率以避免长期积累。 还要注意,用户提供的引用中提到,淘汰策略的触发是在处理命令时检查内存是否超限,然后调用freeMemoryIfNeeded函数进行内存释放。这个过程需要遍历数据库,根据策略选择要淘汰的键。 可能需要比较不同策略的适用场景,比如LRU适合时间局部性好的场景,LFU适合访问频率差异大的情况。同时,随机和TTL适用于特定需求,比如快速淘汰或基于过期时间的优先级。 需要确保回答结构清晰,分点说明每个策略,然后介绍实现方式。同时,根据用户的要求,生成相关问题,比如如何选择策略,如何配置参数,LRU和LFU的区别,以及近似算法的优缺点。 最后,检查是否符合格式要求:使用中文,数学表达式用$...$,独立公式用$$...$$,引用正确标注。不过当前问题可能不涉及数学公式,但如果有涉及的地方需要正确应用格式。</think>### Redis内存淘汰算法机制解析 #### 一、内存淘汰机制简介 当Redis内存使用达到`maxmemory`配置上限时,会触发内存淘汰机制,根据预设策略释放部分内存空间以存储新数据淘汰策略直接影响缓存性能和命中率,需根据业务场景选择。 #### 二、主要淘汰策略及实现方式 1. **随机淘汰(Random)** - **策略**:随机删除键。 - **变种**: - `volatile-random`:仅从设置了过期时间的键中随机删除。 - `allkeys-random`:从所有键中随机删除。 - **实现**:通过随机采样算法快速选择一个键删除,时间复杂度为$O(1)$[^2]。 2. **TTL优先淘汰(Time To Live)** - **策略**:优先删除即将过期的键(TTL值最小)。 - **实现**:遍历过期字典,抽样比较键的剩余存活时间,选择最小的淘汰[^4]。 3. **LRU(最近最少使用)** - **策略**:淘汰最久未被访问的键。 - **Redis实现特点**: - 使用**近似LRU算法**:随机采样$N$个键(默认$N=5$),选择其中最久未访问的淘汰。 - 每个键记录最近访问时间戳`lru`(精度为秒级)。 - **优点**:时间复杂度$O(N)$,性能消耗低[^3]。 4. **LFU(最不经常使用)** - **策略**:淘汰访问频率最低的键。 - **Redis实现特点**: - 基于**频率计数器**:键的`lru`字段被拆分为两部分——低8位记录访问次数,高16位记录衰减时间。 - **衰减机制**:每隔一段时间通过衰减因子降低计数器值,避免旧数据长期占据内存。 - **算法公式**: $$ \text{新频率} = \text{旧频率} \times \text{衰减因子} + \text{本次访问增量} $$ - **适用场景**:热点数据访问频率差异显著时效果更佳。 #### 三、淘汰策略触发流程 1. 客户端执行写入命令时,检查内存是否超过`maxmemory`。 2. 若超限,调用`freeMemoryIfNeeded()`函数,按策略选择键删除。 3. 删除操作同步到AOF和副本节点(若开启持久化)[^5]。 #### 四、策略选择建议 | 策略类型 | 适用场景 | 性能影响 | |-----------------|-----------------------------------|----------| | `volatile-lru` | 需要保留高频访问的持久化数据 | 中等 | | `allkeys-lfu` | 无明确热点数据,需平衡访问频率 | 中等 | | `volatile-ttl` | 依赖TTL管理短期数据(如验证码) | 低 | | `allkeys-random`| 数据重要性均等,追求最快淘汰速度 | 最低 | #### 五、配置示例 ```bash # redis.conf maxmemory 4GB maxmemory-policy allkeys-lfu ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值