type
status
date
slug
summary
tags
category
password
1、概述
Redis是一个内存数据库,所有的数据将保存在内存中,这与传统的 MySQL、Oracle、SqlServer 等关系型数据库直接把数据保存到硬盘相比,Redis的读写效率非常高。但是保存在内存中也有一个很大的缺陷,一旦断电或者宕机,内存数据库中的内容将会全部丢失。为了弥补这一缺陷,Redis 提供了把内存数据持久化到硬盘文件,以及通过备份文件来恢复数据的功能,即 Redis 的持久化机制。有两种方式:
持久化方式 | 描述 | 优点 | 缺点 | 适合场景 |
AOF | 记录所有写操作命令,以追加方式写入文件,重启时重新执行这些命令来恢复数据 | 数据安全,详细记录到每一条写指令 | 文件体积大,数据恢复速度慢 | 文件小,恢复速度快,适合用于定时快照备份和主从全量数据同步的场景。 |
RDB | 按照指定时间间隔生成 Redis 数据库快照,这些快照保存了某个时间点的全量 Redis 数据,重启后使用数据库快照来恢复数据 | 文件体积小,便于快速备份和恢复,启动速度快 | 数据安全性低,隔一段时间才持久化一次,宕机容易造成数据的丢失 | 文件大,但是大部分情况保留了原始的命令操作,适合用于对数据完整性和安全性更高的场景中。 |
另外还有一种混合模式,同时开启 AOF 和 RDB 功能:
- RDB 以一定的频率执行。
- 在两次 RDB 期间,使用 AOF 日志记录这期间的所有命令操作。
- 下一次 RDB 的时候,清空 AOF 日志。
- 在恢复数据的时候,会优先使用 AOF 日志来恢复数据,因为 AOF 保存的文件比 RDB 文件更完整。

2、AOF
Redis 每执行一条写操作命令,就把该命令以追加的方式写入到一个文件里,然后重启 Redis 的时候,先去读取这个文件里的命令,并且执行它,执行完文件的所有命令后数据库就被还原了,这就是 AOF 功能。
注意只会记录写操作命令,读操作命令是不会被记录的。
2.1 开启AOF
第一种方式,通过配置文件开启(推荐),修改 Redis 配置文件
redis.conf
:第二种方式,可以通过 Redis 客户端临时开启 AOF(重启后会失效):
AOF 文件默认保存在 Redis 的工作目录(由
dir
配置指定),文件名为 appendonly.aof
。2.2 AOF的工作过程
AOF 的操作步骤为:
- 命令追加(append):当AOF持久化功能打开时,服务器在执行完一个写命令之后,会以协议格式将被执行的写命令追加到服务器状态的
aof_buf
缓冲区的末尾。(先写内存,再写日志。这样可以保证只有正确执行的命令才会记录到日志上。)
- 文件的写入和同步(sync):根据配置的
appendfsync
同步刷盘策略决定何时调用 Linux 的fsync
或fdatasync
函数将aof_buf
缓冲区的内容保存到磁盘的 AOF 文件中。
2.3 AOF的同步策略
AOF 提供三种同步策略,通过
appendfsync
配置:配置项 | 写回时机 | 优点 | 缺点 |
Always | 每次执行完一个命令之后, write 和 sync 都会被执行。也就是说将aof_buf缓冲区的所有内容写入并同步到AOF文件,每个写命令同步写入磁盘 | 可靠性高,数据基本不丢失 | 性能较差 |
Everysec | 默认策略,sync 每隔一秒钟就会执行一次。也就是说将aof_buf缓存区的内容写入AOF文件,每秒同步一次,该操作由一个线程专门负责。 | 性能适中 | 宕机丢失1s的数据 |
No | write 都会被执行, 但 sync 会被略过。也就是说将aof_buf缓存区的内容写入AOF文件,什么时候同步由操作系统来决定 | 性能好 | 宕机丢失数据多 |
2.4 AOF重写机制
AOF 日志是一个文件,随着执行的写操作命令越来越多,文件的大小会越来越大。如果当 AOF 日志文件过大就会带来性能问题,比如重启 Redis 后,需要读 AOF 文件的内容以恢复数据,如果文件过大,整个恢复的过程就会很慢。
为了解决 AOF 文件体积膨胀的问题,Redis 提供了 AOF 重写机制。当 AOF 文件的大小超过所设定的阈值后,Redis 就会启用 AOF 重写机制,来压缩 AOF 文件。
AOF 重写的触发条件:
- 手动触发:执行
BGREWRITEAOF
命令
- 自动触发:根据配置的
auto-aof-rewrite-percentage
和auto-aof-rewrite-min-size
AOF 重写过程如下:
- 主线程 fork 出一个 bgrewriteaof 子进程,这里的
fork()
使用写时拷贝(copy-on-write)的技术,拷贝出一份数据副本,并建立一个 AOF 重写缓冲区。
- 子进程读取当前数据库状态,生成新的 AOF 文件。注意:AOF 重写不会去读旧的 AOF 文件,而是读取当前数据库中的所有键值对,然后为每一个键值生成一条命令记录到新的 AOF 文件中。

- 子线程重写期间,如果服务器收到了写命令,主线程会先把写命令写到 AOF 缓冲区(这部分数据会写到旧的 AOF 文件,这样保证即使重写失败也能保证数据的安全),然后再写到 AOF 重写缓冲区。
- 子进程重写完成后,通知主线程,主线程收到信号后会把 AOF 重写缓冲区的内容写到新的 AOF 文件(不会直接写到新的 AOF 文件里面,而是先写 AOF 缓冲区,再写回新的 AOF 文件),最后把新的 AOF 文件进行改名,覆盖原有的 AOF 文件。
整个过程只有最后一步是阻塞主线程的,这将 AOF 重写对性能造成的影响降到了最低。

这里涉及到两个知识点:
- AOF 重写缓冲区
- 写时复制(Copy-On-Write)技术
AOF 重写缓冲区是什么
重写 AOF 日志过程中,主进程会继续处理客户端的写命令。如果主进程修改了已经存在 key-value,此时子进程的内存数据就跟主进程的内存数据不一致了。为了解决这种数据不一致问题,Redis 设置了一个 AOF 重写缓冲区,这个缓冲区在创建 bgrewriteaof 子进程之后开始使用。
在重写 AOF 期间,当 Redis 执行完一个写命令之后,它会同时将这个写命令写入到 「AOF 缓冲区」和 「AOF 重写缓冲区」。AOF 缓冲区和 AOF 重写缓冲区的区别:
- AOF 缓冲区:用于临时存储所有写命令,所有的数据先写到 AOF 缓冲区再根据策略同步到磁盘上的 AOF 文件。
- AOF 重写缓冲区:仅在重写期间存在,专门用于存储 AOF 重写期间的新写命令。
联系:当 AOF 重写完成时,主进程在把 AOF 重写缓冲区的数据写到 AOF 缓冲区,再同步到 AOF 文件中。在 AOF 重写过程中,AOF 重写缓存区会一直保持从重写开始到结束所有写命令,而 AOF 缓存区会不断重复【写入—>更新到 AOF 文件—>清除—>写入】这一过程,因而只会保存一部分的命令,所以两者的内容不一致。
写时复制(Copy-On-Write)技术是什么
Redis 在执行
BGSAVE
命令或者 BGREWRITEAOF
命令时,需要 fork 出子进程,调用了 Linux 的 fork()
命令,使用了 COW 技术。COW 的核心思想是:多个进程可以共享同一资源的只读副本,只有当某个进程需要修改这个资源时,系统才会真正执行复制操作,为该进程创建独立的可写副本。
- 父进程使用 COW 技术 fork 出子线程的时候,kernel 把父进程中所有的内存页的权限都设为 read-only,然后子进程的地址空间指向父进程,子线程和父线程共享物理内存空间。
- 当父进程写数据的时候,在写入数据的内存页上触发页中断异常,CPU 硬件检测到内存页是 read-only 的,于是触发页异常中断(page-fault),陷入 kernel 的一个中断例程。中断例程中,kernel 就会把触发的异常的页复制一份。这时候主进程再把要修改的数据写入到新的物理页,子进程仍然指向旧的物理页数据(说明子线程备份的是旧数据)。

注意:
- fork() 完之后,父子进程只是分配的虚拟内存空间不同,对应的物理内存空间是相同的。只有父进程会在写入时才会触发 COW 机制。没有触发异常的其他内存页依然是父子进程共享同一个物理页。
- 子进程写到备份文件的数据是执行了 fork() 函数那一时刻的数据,后面的更新都不会备份。
- 总体来看,Redis还是读操作比较多。如果子进程存在期间,发生了大量的写操作,那可能就会出现很多的分页错误(页异常中断 page-fault),这样就得耗费不少性能在复制上。而在 rehash 阶段上,写操作是无法避免的。所以 Redis 在 fork 出子进程之后,将负载因子阈值提高,尽量减少写操作,避免不必要的内存写入操作,最大限度地节约内存。
优点:
- 可以快速完成线程 fork 工作,不会阻塞主线程太长时间。
- 可以减少不必要的内存资源分配。
缺点:
- 写操作可能比传统方式稍慢(需要先复制)
- 父线程如果产生大量写操作,会产生大量的页中断异常,也会造成性能损失。
应用场景:
- Btrfs 和 ZFS 等现代文件系统
- Docker 等容器平台的文件系统(如 aufs、overlayfs)
3、RDB
RDB 通过创建某个时间点的数据快照(RDB 文件)来实现持久化。RDB 文件是压缩的二进制文件,默认名为
dump.rdb
,保存着 Redis 某个时间点的全量数据。注意:执行快照是一个比较重的操作,如果频率太频繁,可能会对 Redis 性能产生影响。如果频率太低,服务器故障时,丢失的数据会更多。通常可能设置至少 5 分钟才保存一次快照,这时如果 Redis 出现宕机等情况,则意味着最多可能丢失 5 分钟数据。
3.1 开启RDB
第一种方式,通过修改
redis.conf
配置文件自动触发。底层通过服务器周期函数 serverCron 来触发,该函数每 100 毫秒执行一次。默认配置如下所示第二种方式,使用 Redis 命令手动触发。Redis 提供了两个命令来生成 RDB 文件,分别是
SAVE
和 BGSAVE
,他们的区别就在于是否在「主线程」里执行:SAVE
命令:在主线程生成 RDB 文件,由于和执行操作命令在同一个线程,所以如果写入 RDB 文件的时间太长,会阻塞主线程;
BGSAVE
命令:会创建一个子进程来生成 RDB 文件,这样可以避免主线程的阻塞。
验证 RDB 是否工作
在输出中查找与RDB相关的信息,如:
rdb_last_save_time
:上次成功保存的时间戳
rdb_last_bgsave_status
:上次后台保存状态
3.2 RDB的工作过程
不管是自动触发还是手动触发创建 RDB 文件,最终都会调用 rdbSave 函数来创建 RDB 文件。
以最常用的
BGSAVE
命令为例,执行过程如下:- 主线程 fork 出一个
BGSAVE
子进程,这里的fork()
同样使用写时拷贝(copy-on-write)的技术,拷贝出一份数据副本。
- 子进程把数据写到一个临时的 RDB 文件。
- 当子进程写完新的 RDB 文件后,把旧的 RDB 文件替换掉。

注意:
- 为了避免产生竞争条件,
BGSAVE
执行时,SAVE
命令不能执行。
- 为了避免性能问题,
BGSAVE
和BGREWRITEAOF
不能同时执行。
- RDB 文件的加载工作是在服务器启动时自动执行的,Redis 并没有提供专门用于加载 RDB 文件的命令。
- 调用 rdbLoad 函数载入 RDB 文件时,不能进行任何和数据库相关的操作,不过订阅与发布方面的命令可以正常执行,因为它们和数据库不相关联。
- 如果服务器在启动时, 打开了 AOF 功能, 那么程序优先使用 AOF 文件来还原数据。 只有在 AOF 功能未打开的情况下, Redis 才会使用 RDB 文件来还原数据。这是因为在服务器发生故障时,RDB 丢失的数据会比 AOF 持久化的方式更多,因为 RDB 快照是全量快照的方式,因此执行的频率不能太频繁,否则会影响 Redis 性能,而 AOF 日志可以以秒级的方式记录操作命令,所以丢失的数据就相对更少。
- Author:mcbilla
- URL:http://mcbilla.com/article/ddfadf35-f56a-4957-9c5b-3531c46682f1
- Copyright:All articles in this blog, except for special statements, adopt BY-NC-SA agreement. Please indicate the source!