Redis 持久化
全面解析 Redis 持久化:RDB、AOF与混合持久化 - 江小康 - 博客园
AOF 日志
Append only file
Redis 在执行完一条写操作命令后,就会把该命令以追加的方式写入到一个文件里,然后 Redis 重启时,会读取该文件记录的命令,然后逐一执行命令的方式来进行数据恢复。
AOF 日志写入(Append-Only File)主要由主线程负责,但 AOF 的 重写(Rewrite) 是在 子线程(子进程) 中进行的。
以 Redis 协议(RESP) 格式存储,便于快速解析和恢复。记录方式:
1 | *[命令有多少个部分] |
写回模式
在 Redis 中,AOF(Append-Only File)日志写入涉及两个不同的缓冲区:
- AOF 缓冲区(aof_buf):Redis 用户态的缓冲区,存储命令文本
- 内核缓冲区:操作系统(内核态)的页缓存,存储 AOF 文件二进制数据(在调用
write
后进入,需要调用fsync
才会写入磁盘)
1 | Redis 主线程 -> AOF 缓冲区 (aof_buf) -> 内核缓冲区(Page Cache) -> 磁盘 |
3中写回模式:
- Always,每次写操作命令后同步写回磁盘
- EverySec:每次写操作命令写进
aof_buf
,每隔一秒写入内核缓冲区并写回一次磁盘(write -> free aof_buf -> fsync
),最多丢失一秒数据。 - No:由操作系统决定何时写回磁盘,Redis 只写入 AOF 的内核缓冲区。可能会丢失大量数据。
AOF 重写(异步进行)
当 AOF 文件的大小超过所设定的阈值后,Redis 就会启用 AOF 重写机制,扫描数据库中所有数据,逐一把内存数据的键值对转换成一条命令,再将命令记录到重写日志。AOF 重写是通过 「分叉子进程」(fork()
)的方式在 单独的子进程(而不是线程) 中执行的。
Note
Fork
fork()
是操作系统的系统调用,用于从 当前进程(父进程) 创建一个 新进程(子进程)。子进程是父进程的 「副本」,它继承了父进程的大部分资源:
- 虚拟地址空间(主进程的页表)
- 文件描述符
- 环境变量
- 程序代码
子进程和父进程几乎完全相同,但它们有一个关键区别:
- 子进程的
PID(进程 ID)
是唯一的,且不同于父进程。
在内核中,fork()
使用 「写时复制(Copy-On-Write, COW)」 技术:
- 父子进程最初共享相同的内存页面,避免立即复制所有内存数据(高效);
- 只有在父子进程中的某个进程尝试修改内存时,才会将要修改的内存进行内存复制,分配独立的页面避免相互影响。
- 最多可能导致内存占用翻倍,要多注意。
重写过程中的新命令
**主线程:**收到新的写命令:
- 将命令同时写入 **AOF 缓冲区(
aof_buf
)**和 AOF 重写缓冲区(aof_rewrite_buf
)。 - 重写完成后主进程将 AOF 重写缓冲区的内容写入到新的 AOF 文件中
读取当前数据库中的所有键值对,然后将每一个键值对用一条命令记录到「新的 AOF 文件」,等到全部记录完后,就将新的 AOF 文件替换掉现有的 AOF 文件。
RDB 快照
.rdb
RDB 文件的内容是二进制数据。
Redis 提供了两个命令来生成 RDB 文件,分别是 save
(主线程阻塞执行)和 bgsave
(fork
子线程执行)。Redis 的快照是全量快照,也就是说每次执行快照,都是把内存中的「所有数据」都记录到磁盘中。
Redis 使用 bgsave
对当前内存中的所有数据做快照,发生了写时复制后,RDB 快照保存的是原本的内存数据,而主线程刚修改的数据只能交由下一次的 bgsave
快照。