1. 引言
Redis的核心特点可以用一个字概括:快! 其高速性能的关键原因之一,在于它的所有核心数据均直接存储在内存中。主线程执行命令时,无需与磁盘进行交互,从而彻底规避了磁盘IO(输入/输出)带来的性能损耗——要知道,磁盘IO的速度远低于内存操作,是传统存储方案中常见的性能瓶颈。
但内存存在一个天然缺陷:它属于带电易失性存储。一旦服务器重启、意外宕机或遭遇断电,内存中的所有数据都会直接丢失。这意味着,若仅将数据存于内存而不做任何持久化处理,数据的可靠性将面临巨大挑战,甚至可能造成不可挽回的数据损失。
为了解决这一问题,Redis专门提供了RDB(Redis DataBase,快照持久化) 和AOF(Append-Only File,只追加文件)两种持久化机制。它们的核心原理是将内存中的数据定期或实时同步到磁盘文件中;当服务器重启时,Redis再从这些磁盘文件中读取数据并恢复到内存,以此保障数据在故障后仍能有效留存。
2. RDB持久化
RDB(Redis DataBase)是 Redis 默认的持久化方式,其核心原理是在指定时间间隔内,将内存中的全量数据生成一份二进制快照文件(.rdb)并写入磁盘,类似给数据 “拍照片”。
2.1 RDB的工作原理
RDB的触发分为两类,一类是手动触发,另一类是自动触发。
手动触发可以通过以下两个命令来完成:
-
SAVE: 会阻塞主线程。
-
BGSAVE: 主线程会fork出一个子进程来给内存中的数据形成快照,并写入到磁盘文件中。
执行SAVE命令时,Redis的主线程会亲自执行RDB快照的创建和写入磁盘的全部工作。如果Redis中的数据量较大,那么将极有可能阻塞主线程,在这期间,Redis服务器将无法处理任何其他请求,导致服务暂时不可用。所以,生产环境上是绝对不允许使用SAVE的。
执行BGSAVE命令时,Redis的主线程会fork出一个子进程来给内存中的数据形成快照,并将新生成的RDB文件写入磁盘。在fork的过程中,操作系统会将页表复制一份给子进程,此时父子进程指向的是同一块物理内存。这些物理页被操作系统标为只读,当有一方要进行写操作时,将会触发写时拷贝,复制一份当前被写入的物理页,执行写操作的一方,就针对这个副本进行修改。如果Redis中的数据量比较大,拷贝页表的开销就会比较大,可能会出现短暂的阻塞。但是,相对于SAVE的长时间阻塞来说,这个影响几乎忽略不计。

这是Redis为空时,dump.rdb( 在/var/lib/redis路径下)文件的内容。下面我们尝试插入几个数据,然后执行bgsave命令,接着再次查看dump.rdb 文件。


执行BGSAVE命令后,再次查看dump.rdb文件,可以看到我们插入的key值,但是不完全看懂,因为这是二进制数据。
在dump.rdb文件中查到了新插入的key值,意味着Redis已经进行持久化了,即使此时Redis崩溃,数据也不会丢失。
我们来演示一下:
- 查看系统中正在运行的
redis-server - 干掉我们客户端连接的
redis-server redis-server关闭后,会自动立刻重启- 我们查看在
redis-server关闭前写入的数据是否还在 - 如果数据还在,意味着持久化是成功的

前后两次查到的进程pid不一样,说明Redis已经重启了。下面我们重新连接Redis服务器,看看数据有没有丢失。

可以看到重启之后,数据还是能够恢复的。
关于自动触发RDB,我们可以看看Redis的配置文件(在/etc/redis路径下)。

#规则:save <seconds> <changes>
save 3600 1 # 3600秒内有1次写操作,触发RDB
save 300 100 # 300秒内有100次写操作,触发RDB
save 60 10000 # 60秒内有10000次写操作,触发RDB
这些触发规则,都是可配置的。而且,这些通过配置文件自动触发的机制,底层也是调用BGSAVE命令来完成的。
BGSAVE的执行流程。

2.2 RDB优缺点
优点 :
-
文件体积小:二进制压缩格式,占用磁盘空间少;
-
恢复速度快:加载二进制文件直接入内存,适合大型数据恢复;
-
性能影响小:BGSAVE 异步执行,主线程阻塞少。
缺点:
-
数据安全性:快照间隔内宕机,会丢失该间隔的所有数据(如配置 60 秒 / 10000 次写,若 59 秒宕机,丢失 59 秒数据);
-
fork 成本高:数据量大时,fork 子进程会消耗大量内存(需复制页表),且可能阻塞主线程;
-
不适合实时备份:无法做到 “秒级” 数据不丢失。
我们通过修改配置文件,将 RDB 的触发条件设置为 save 1 1(1 秒内有 1 次写操作就触发 RDB),这样不就做到“秒级” 数据不丢失了吗?
理论上看起来可行,但在生产环境中完全不可行,甚至是一场灾难。
RDB的核心机制是全量快照,必须通过fork()子进程来完成。
- 虽然fork()很快,但它不是零耗时的。当数据量特别大的时候,fork()是会有短暂的阻塞的。如果每秒都来这么一出,在高并发场景下,会导致请求堆积,Redis的响应延迟飙升。
- 可能会导致磁盘I/O过载。RDB文件通常比较大,每秒都要将几十兆甚至几百兆的数据写入磁盘,会瞬间占满磁盘带宽。这时候,其他业务的读写请求会因为争抢I/O资源而变得很慢。
- 每次fork()都会触发
写时拷贝,如果写入频繁,父子进程共享的内存也会被大量复制,占用更多的内存,容易触发内存溢出,导致Redis崩溃。
下面,我们来看看另一种方式的持久化策略。
3. AOF持久化
AOF(Append Only File)是 Redis 的另一类持久化机制,核心原理是将每一条 “写操作命令”(如 SET、HSET、LPUSH 等)以文本格式追加到 AOF 文件中,类似 “日志记录”。Redis 重启时,通过重新执行 AOF 文件中的命令,即可恢复内存数据。
默认情况下,AOF机制并没有打开,下面我们将它打开(no改为yes),然后重启Redis使其生效,配置文件在/etc/redis/目录下。

3.1 AOF的工作原理
AOF(Append Only File)的核心思想是:记录Redis收到的每一条写命令(读命令不记录),并在重启时通过重新执行这些命令来恢复数据。 工作原理可以拆分为 命令追加(Append)、文件刷盘(Sync)、文件重写(Rewrite) 三大流程,确保数据不丢失与 文件不膨胀。
-
命令追加: 主线程处理每一条
写操作命令时,先执行命令修改内存数据,再将命令追加到AOF 缓冲区(在内存中,避免频繁写磁盘)。这个过程非常快,不会阻塞主线程处理其他请求。 -
文件刷盘(Sync): 文件刷盘操作,是用来保证数据的安全性的。Redis通过
appendfsync配置项来决定何时将缓冲区中的数据刷入磁盘。下面是Redis的3种刷盘策略:- appendfsync always:每次写命令都立即刷盘
- appendfsync everysec:每秒刷盘一次
- appendfsync no:由操作系统决定何时刷盘
如果是always,那么整个刷盘操作将由主线程调用系统调用fsync()完成,数据真正落盘后,主线程才会返回响应给客户端。所以,这个策略是会阻塞主线程的。除非是不能容忍少量数据丢失的场景才会使用。
如果是everysec,那么主线程只需要把写命令追加到内存中的AOF缓冲区。剩下的刷盘操作,由后台线程调用系统调用fsync()完成。
如果是no,那么何时刷盘,完全由操作系统决定,这个策略丢失的数据是最多的。
通常情况下,使用的是everysec这种刷盘策略。
- 文件重写(Rewrite): 用来解决文件膨胀问题的。AOF 是不断追加日志的。如果对一个 Key 修改了 100 次,AOF 文件里就会记录 100 条命令。随着时间推移,文件会越来越大,重启恢复数据也会变得极慢。
127.0.0.1:6379> set key 111 127.0.0.1:6379> set key 222 127.0.0.1:6379> set key 333 127.0.0.1:6379> set key 444
上面执行了4条命令,但是最终结果只有最后一条有用,因为前面的数据都被覆盖掉了。这种情况下,如果不加以斟酌,直接把这4条命令全部刷到磁盘文件中,必然会导致文件膨胀,浪费空间(前3条命令是没必要的)。
为了解决文件膨胀问题,引入了重写机制。
Redis 会 fork 一个子进程,读取当前内存中的数据,生成一套最简命令集来还原这些数据。比如我们对 count 键执行了 100 次 INCR,重写后,AOF 文件里只会保留一条 SET count 100。
文件重写的2种触发方式:
-
手动触发,通过命令
bgrewriteaof来触发。 -
自动触发。通过配置 “AOF 文件大小增长率” 和 “最小文件体积” 触发。
自动触发的配置项:
auto-aof-rewrite-percentage100 # AOF 文件大小比上次重写后增长 100%(即翻倍)auto-aof-rewrite-min-size64mb # AOF 文件最小达到 64MB 才触发重写(避免小文件频繁重写)
以上两个条件同时满足时,就会自动触发文件重写。简单来说,就是当AOF文件足够大,而且比上一次重写后增大了指定比例时,Redis就会自动触发重写。
日志重写的流程:
-
触发重写。
-
主线程fork()出一个子进程。子进程读取当前内存数据,生成
精简命令集,并写入临时 AOF 文件。 -
重写期间,主线程将新的写命令同时追加到
旧 AOF 缓冲区和重写缓冲区(确保重写期间的新命令不丢失)。 -
子进程完成重写后,主线程将
重写缓冲区的命令追加到临时AOF文件,然后用临时文件替换旧 AOF 文件。
临时的AOF文件是在磁盘中的,主线程要追加重写缓冲区中的命令的话,就需要访问磁盘,调用完write()后,调用fsync()系统调用强制刷盘。虽然有磁盘I/O,但是整个fork()到重写完成期间新增的命令很小,这点数据写入磁盘对性能几乎没有影响。

为什么在重写期间,主线程还要往旧的AOF缓冲区中写入命令,只写入重写缓冲区不就好了吗?
重写期间,主线程将新的写命令追加到重写缓冲区。子进程重写完成后,主线程将重写缓冲区的命令追加到临时的AOF文件,是为了避免重写期间的命令丢失。
重写期间,还要坚持往旧的AOF缓冲区中写入新的命令,是因为子进程重写形成临时的AOF文件需要时间,在新的AOF文件替换旧AOF文件之前,旧的AOF文件仍然是Redis数据恢复的可靠依据。
假设重写期间不向旧的AOF缓冲区中添加新的命令:如果重写过程中,Redis意外宕机,新AOF文件尚未生成或不完整,而旧文件又缺少重写期间的新命令,将造成这部分数据永久丢失。
3.2 AOF的优缺点
优点 :
-
数据安全性高:支持秒级(everysec)或实时(always)刷盘,丢失数据少;
-
文件可读性强:文本格式,可手动修改或恢复(如误删数据,可编辑 AOF 删除误操作命令);
-
增量记录:仅追加写命令,无全量数据快照的性能开销。
缺点:
-
文件体积大:文本格式且记录所有命令,磁盘占用远高于 RDB;
-
恢复速度慢:重启时需重新执行所有命令,数据量大时恢复时间长;
-
刷盘性能损耗:always 策略会频繁触发 I/O,影响主线程性能(always策略下,由主线程调用fsync()完成刷盘)。
4. 总结
RDB持久化策略,生成二进制数据,恢复快,文件体积小,但是数据的安全性做不到秒级丢失。
AOF持久化策略,生成文本文件,追击的方式写入AOF文件,文件体积容易膨胀,恢复是需要重新执行命令,速度慢,但是可以做到秒级数据丢失。
所以,具体使用哪种策略,需要根据实际的场景,权衡利弊之后才能做出选择。而且,也不是只能二选一,混着用,利用各自的优势也是一种很好的办法。

2741

被折叠的 条评论
为什么被折叠?



