Redis的两种持久化方式


1. 引言

Redis的核心特点可以用一个字概括:快! 其高速性能的关键原因之一,在于它的所有核心数据均直接存储在内存中。主线程执行命令时,无需与磁盘进行交互,从而彻底规避了磁盘IO(输入/输出)带来的性能损耗——要知道,磁盘IO的速度远低于内存操作,是传统存储方案中常见的性能瓶颈。

但内存存在一个天然缺陷:它属于带电易失性存储。一旦服务器重启、意外宕机或遭遇断电,内存中的所有数据都会直接丢失。这意味着,若仅将数据存于内存而不做任何持久化处理,数据的可靠性将面临巨大挑战,甚至可能造成不可挽回的数据损失。

为了解决这一问题,Redis专门提供了RDB(Redis DataBase,快照持久化) 和AOF(Append-Only File,只追加文件)两种持久化机制。它们的核心原理是将内存中的数据定期或实时同步到磁盘文件中;当服务器重启时,Redis再从这些磁盘文件中读取数据并恢复到内存,以此保障数据在故障后仍能有效留存。

2. RDB持久化

RDB(Redis DataBase)是 Redis 默认的持久化方式,其核心原理是在指定时间间隔内,将内存中的全量数据生成一份二进制快照文件(.rdb)并写入磁盘,类似给数据 “拍照片”。

2.1 RDB的工作原理

RDB的触发分为两类,一类是手动触发,另一类是自动触发。

手动触发可以通过以下两个命令来完成:

  1. SAVE: 会阻塞主线程。

  2. BGSAVE: 主线程会fork出一个子进程来给内存中的数据形成快照,并写入到磁盘文件中。

执行SAVE命令时,Redis的主线程会亲自执行RDB快照的创建和写入磁盘的全部工作。如果Redis中的数据量较大,那么将极有可能阻塞主线程,在这期间,Redis服务器将无法处理任何其他请求,导致服务暂时不可用。所以,生产环境上是绝对不允许使用SAVE的。

执行BGSAVE命令时,Redis的主线程会fork出一个子进程来给内存中的数据形成快照,并将新生成的RDB文件写入磁盘。在fork的过程中,操作系统会将页表复制一份给子进程,此时父子进程指向的是同一块物理内存。这些物理页被操作系统标为只读,当有一方要进行写操作时,将会触发写时拷贝,复制一份当前被写入的物理页,执行写操作的一方,就针对这个副本进行修改。如果Redis中的数据量比较大,拷贝页表的开销就会比较大,可能会出现短暂的阻塞。但是,相对于SAVE的长时间阻塞来说,这个影响几乎忽略不计。

dump.rdb
这是Redis为空时,dump.rdb( 在/var/lib/redis路径下)文件的内容。下面我们尝试插入几个数据,然后执行bgsave命令,接着再次查看dump.rdb 文件。
BGSAVE
dump.rdb
执行BGSAVE命令后,再次查看dump.rdb文件,可以看到我们插入的key值,但是不完全看懂,因为这是二进制数据。
dump.rdb文件中查到了新插入的key值,意味着Redis已经进行持久化了,即使此时Redis崩溃,数据也不会丢失。
我们来演示一下:

  1. 查看系统中正在运行的redis-server
  2. 干掉我们客户端连接的redis-server
  3. redis-server 关闭后,会自动立刻重启
  4. 我们查看在redis-server关闭前写入的数据是否还在
  5. 如果数据还在,意味着持久化是成功的

redis

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

在这里插入图片描述

可以看到重启之后,数据还是能够恢复的。

关于自动触发RDB,我们可以看看Redis的配置文件(在/etc/redis路径下)。

rdb

#规则:save <seconds> <changes>
save 3600 1    # 3600秒内有1次写操作,触发RDB
save 300 100   # 300秒内有100次写操作,触发RDB
save 60 10000  # 60秒内有10000次写操作,触发RDB

这些触发规则,都是可配置的。而且,这些通过配置文件自动触发的机制,底层也是调用BGSAVE命令来完成的。

BGSAVE的执行流程。

BGSAVE

2.2 RDB优缺点

优点 :

  1. 文件体积小:二进制压缩格式,占用磁盘空间少;

  2. 恢复速度快:加载二进制文件直接入内存,适合大型数据恢复;

  3. 性能影响小:BGSAVE 异步执行,主线程阻塞少。

缺点:

  1. 数据安全性:快照间隔内宕机,会丢失该间隔的所有数据(如配置 60 秒 / 10000 次写,若 59 秒宕机,丢失 59 秒数据);

  2. fork 成本高:数据量大时,fork 子进程会消耗大量内存(需复制页表),且可能阻塞主线程;

  3. 不适合实时备份:无法做到 “秒级” 数据不丢失。

我们通过修改配置文件,将 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/目录下。

aof

3.1 AOF的工作原理

AOF(Append Only File)的核心思想是:记录Redis收到的每一条写命令(读命令不记录),并在重启时通过重新执行这些命令来恢复数据。 工作原理可以拆分为 命令追加(Append)文件刷盘(Sync)文件重写(Rewrite) 三大流程,确保数据不丢失文件不膨胀

  1. 命令追加: 主线程处理每一条写操作命令时,先执行命令修改内存数据,再将命令追加到 AOF 缓冲区(在内存中,避免频繁写磁盘)。这个过程非常快,不会阻塞主线程处理其他请求。

  2. 文件刷盘(Sync): 文件刷盘操作,是用来保证数据的安全性的。Redis通过appendfsync配置项来决定何时将缓冲区中的数据刷入磁盘。下面是Redis的3种刷盘策略:

    • appendfsync always:每次写命令都立即刷盘
    • appendfsync everysec:每秒刷盘一次
    • appendfsync no:由操作系统决定何时刷盘

如果是always,那么整个刷盘操作将由主线程调用系统调用fsync()完成,数据真正落盘后,主线程才会返回响应给客户端。所以,这个策略是会阻塞主线程的。除非是不能容忍少量数据丢失的场景才会使用。

如果是everysec,那么主线程只需要把写命令追加到内存中的AOF缓冲区。剩下的刷盘操作,由后台线程调用系统调用fsync()完成。

如果是no,那么何时刷盘,完全由操作系统决定,这个策略丢失的数据是最多的。

通常情况下,使用的是everysec这种刷盘策略。

  1. 文件重写(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种触发方式:

  1. 手动触发,通过命令bgrewriteaof来触发。

  2. 自动触发。通过配置 “AOF 文件大小增长率” 和 “最小文件体积” 触发。

自动触发的配置项:

  • auto-aof-rewrite-percentage 100 # AOF 文件大小比上次重写后增长 100%(即翻倍)
  • auto-aof-rewrite-min-size 64mb # AOF 文件最小达到 64MB 才触发重写(避免小文件频繁重写)

以上两个条件同时满足时,就会自动触发文件重写。简单来说,就是当AOF文件足够大,而且比上一次重写后增大了指定比例时,Redis就会自动触发重写。

日志重写的流程:

  1. 触发重写。

  2. 主线程fork()出一个子进程。子进程读取当前内存数据,生成精简命令集,并写入临时 AOF 文件。

  3. 重写期间,主线程将新的写命令同时追加到旧 AOF 缓冲区重写缓冲区(确保重写期间的新命令不丢失)。

  4. 子进程完成重写后,主线程将重写缓冲区的命令追加到临时AOF文件,然后用临时文件替换旧 AOF 文件。

临时的AOF文件是在磁盘中的,主线程要追加重写缓冲区中的命令的话,就需要访问磁盘,调用完write()后,调用fsync()系统调用强制刷盘。虽然有磁盘I/O,但是整个fork()到重写完成期间新增的命令很小,这点数据写入磁盘对性能几乎没有影响。

文件重写

为什么在重写期间,主线程还要往旧的AOF缓冲区中写入命令,只写入重写缓冲区不就好了吗?

重写期间,主线程将新的写命令追加到重写缓冲区。子进程重写完成后,主线程将重写缓冲区的命令追加到临时的AOF文件,是为了避免重写期间的命令丢失。

重写期间,还要坚持往旧的AOF缓冲区中写入新的命令,是因为子进程重写形成临时的AOF文件需要时间,在新的AOF文件替换旧AOF文件之前,旧的AOF文件仍然是Redis数据恢复的可靠依据。

假设重写期间不向旧的AOF缓冲区中添加新的命令:如果重写过程中,Redis意外宕机,新AOF文件尚未生成或不完整,而旧文件又缺少重写期间的新命令,将造成这部分数据永久丢失。

3.2 AOF的优缺点

优点 :

  1. 数据安全性高:支持秒级(everysec)或实时(always)刷盘,丢失数据少;

  2. 文件可读性强:文本格式,可手动修改或恢复(如误删数据,可编辑 AOF 删除误操作命令);

  3. 增量记录:仅追加写命令,无全量数据快照的性能开销。

缺点:

  1. 文件体积大:文本格式且记录所有命令,磁盘占用远高于 RDB;

  2. 恢复速度慢:重启时需重新执行所有命令,数据量大时恢复时间长;

  3. 刷盘性能损耗:always 策略会频繁触发 I/O,影响主线程性能(always策略下,由主线程调用fsync()完成刷盘)。

4. 总结

RDB持久化策略,生成二进制数据,恢复快,文件体积小,但是数据的安全性做不到秒级丢失。
AOF持久化策略,生成文本文件,追击的方式写入AOF文件,文件体积容易膨胀,恢复是需要重新执行命令,速度慢,但是可以做到秒级数据丢失。
所以,具体使用哪种策略,需要根据实际的场景,权衡利弊之后才能做出选择。而且,也不是只能二选一,混着用,利用各自的优势也是一种很好的办法。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值