Redis持久化RDB和AOF实现原理详细介绍
文章目录
- 一、RDB持久化
- 1. RDB核心原理
- 2. RDB触发方式
- 2.1 手动触发
- 2.2 自动触发
- 2.3 其他方式触发
- 3. RDB文件生成
- 4. RDB文件加载
- 5. RDB文件结构
- 6. RDB文件解析
- 7. RDB参数配置
- 8. RDB优缺点
- 二、AOF持久化
- 1. AOF核心原理
- 2. AOF同步策略
- 3. AOF文件生成
- 4. AOF文件加载
- 5. AOF文件结构
- 6. AOF文件重写
- 6.1 AOF重写实现原理
- 6.2 AOF重写触发方式
- 6.3 AOF文件重写流程
- 7. AOF参数配置
- 8. AOF优缺点
- 三、RDB 与 AOF 对比
- 四、混合持久化(AOF + RDB)
- 1. 混合持久化的核心概念
- 2. 混合持久化的工作原理
- 2.1 触发条件
- 2.2 重写流程
- 2.3 文件结构
- 2.4 重启加载流程
- 3. 混合持久化参数配置
- 4. 混合持久化适用场景
- 5. 混合持久化的优缺点
- 总结
Redis 作为内存数据库,数据默认存储在内存中。为了防止服务器宕机导致数据丢失,Redis 提供了两种持久化机制:RDB(快照)和AOF(日志追加),以及从Redis 4.0+开始支持的 混合持久化 。以下是Redis持久化的详细介绍:
一、RDB持久化
1. RDB核心原理
RDB 通过定时生成内存快照,将 Redis 内存中的数据以二进制格式保存到磁盘文件(默认文件名:dump.rdb
),服务器重启时自动读取RDB文件恢复数据。
2. RDB触发方式
2.1 手动触发
Redis 有两个命令可以用于生成RDB文件,一个是SAVE
,另一个是BGSAVE
。
SAVE
:阻塞主进程,直到快照完成(生产环境慎用)。- 当
SAVE
命令执行时,服务器会拒绝所有客户端请求,直到完成。
- 当
BGSAVE
:fork子进程异步执行快照,主进程继续处理请求(推荐)。- 当
BGSAVE
命令执行时,服务器可以继续处理客户端请求。 - 当
BGSAVE
命令执行时,客户端发送的SAVE
命令会被服务器拒绝,服务器禁止SAVE
命令和BGSAVE
命令同时执行,避免主线程、子进程同时执行两个rdbSave
调用,防止产生竞争条件。 - 当
BGSAVE
命令执行时,客户端发送的BGSAVE
命令会被服务器拒绝,因为同时执行两个BGSAVE
命令也会产生竞争条件。 - 当
BGSAVE
命令执行时,客户端发送的BGREWRITEAOF
命令会被延迟到BGSAVE
命令执行完毕之后执行。 - 如果
BGREWRITEAOF
命令正在执行,客户端发送的BGSAVE
命令会被服务器拒绝。
- 当
2.2 自动触发
通过配置文件redis.conf
中的save
参数设置规则:
save 900 1 # 900秒内至少1次数据变更则触发
save 300 10 # 300秒内至少10次变更
save 60 10000 # 60秒内至少10000次变更
2.3 其他方式触发
主从复制时主节点自动触发、执行SHUTDOWN
命令时触发。
3. RDB文件生成
RDB文件生成通过SAVE
和BGSAVE
两种操作,但是因为SAVE
操作会暂停服务,所以一般情况下都是BGSAVE
触发生成RDB文件。以下是RDB文件BGSAVE
触发生成的详细步骤:
- 服务器主进程首先判断:当前是否在执行 save/bgsave/bgrewriteaof 的子进程,如果在执行则 bgsave 命令直接返回。bgsave/bgrewriteaof 的子进程不能同时执行,主要是基于性能方面的考虑:两个并发的子进程同时执行大量的磁盘写操作,可能引起严重的性能问题。
- 主进程执行 fork 操作创建子进程,这个过程中主进程是阻塞的,Redis 不能执行来自客户端的任何命令。
- 主进程 fork 后,bgsave 命令返回”Background saving started”信息并不再阻塞主进程,并可以响应其他命令。
- 子进程创建 RDB 文件,根据主进程内存快照生成临时快照文件,完成后对原有文件进行原子替换。
- 子进程发送信号给主进程表示完成,主进程更新统计信息。
4. RDB文件加载
Redis 服务器会在启动的时候自动执行载入程序,判断AOF是否开启,进行数据的恢复。
5. RDB文件结构
REDIS
:固定值,保存着REDIS
五个字符,长度为5字节。通过这五个字符,程序可以在载入文件时,快速检查所载入的文件是否RDB文件。db_version
:记录了RDB文件的版本号,长度为4字节的字符串整数。比如"0006"就代表RDB文件的版本为第六版。database 0~N
:记录了Redis的非空数据库及全部数据信息。如果数据库状态为空(所有数据库都是空的),那么这个部分也为空,长度为0字节。EOF
:标志着RDB文件正文内容的结束,固定值,长度为1字节。check_sum
:记录了一个8字节长的无符号整数校验和,这个校验和是程序通过对REDIS、db_version、databases、EOF四个部分的内容进行计算得出的。服务器在载入RDB文件时,会将载入数据所计算出的校验和与check_sum所记录的校验和进行对比,以此来检查RDB文件是否有出错或者损坏的情况出现。SELECTDB
:固定值,长度为1字节,当读入程序遇到这个值的时候,它知道接下来要读入的将是一个数据库号码。db_number
:记录了一个数据库编号,根据号码的大小不同,这个部分的长度可以是1字节、2字节或者5字节。当程序读入db_number部分之后,服务器会调用SELECT命令,根据读入的数据库号码进行数据库切换,使得之后读入的键值对可以载入到正确的数据库中。key_value_pairs
:保存了数据库中的所有键值对数据,如果键值对带有过期时间,那么过期时间也会和键值对保存在一起。根据键值对的数量、类型、内容以及是否有过期时间等条件的不同,key_value_pairs部分的长度也会有所不同。EXPIRETIME_MS
:固定值,长度为1字节,它告知读入程序,接下来要读入的将是一个以毫秒为单位的过期时间。如果没有设置过期时间则无此字段。ms
:记录了一个8字节长的带符号整数,内容为一个以毫秒为单位的UNIX时间戳,这个时间戳就是键值对的过期时间。如果没有设置过期时间则无此字段。TYPE
:记录了value的类型,长度为1字节,值可以是以下常量的其中
一个,与Redis对象或底层数据结构(REDIS_ENCODING_*编码)对应:- REDIS_RDB_TYPE_STRING
- REDIS_RDB_TYPE_LIST
- REDIS_RDB_TYPE_SET
- REDIS_RDB_TYPE_ZSET
- REDIS_RDB_TYPE_HASH
- REDIS_RDB_TYPE_LIST_ZIPLIST
- REDIS_RDB_TYPE_SET_INTSET
- REDIS_RDB_TYPE_ZSET_ZIPLIST
- REDIS_RDB_TYPE_HASH_ZIPLIST
key
:记录了一个字符串对象,它的编码方式和REDIS_RDB_TYPE_STRING类型的value一样。根据内容长度的不同,key的长度也会有所不同。value
:记录了一个值对象,每个值对象的类型都由与之对应的TYPE记录,根据类型的不同,value部分的结构、长度也会有所不同。
6. RDB文件解析
以下为一个包含带有过期时间的字符串键的RDB文件简单示例。
写入数据:
redis> FLUSHALL
OK
redis> SETEX MSG 10086 "HELLO"
OK
redis> SAVE
OK
打印RDB文件:
$ od -c dump.rdb
0000000 R E D I S 0 0 0 6 376 \0 374 \ 2 365 336
0000020 @ 001 \0 \0 \0 003 M S G 005 H E L L O 377
0000040 212 231 x 247 252 } 021 306
0000050
RDB文件各个部分的意义:
R E D I S 0 0 0 6
:RDB文件标志和版本号。376\0
:切换到0号数据库。374
:代表特殊值EXPIRETIME_MS。\2 365 336@001\0\0
:代表八字节长的过期时间。\0 003 M S G
:\0表示这是一个字符串键,003是键的长度,MSG是键。005 H E L L O
:005是值的长度,HELLO是值。377
:代表EOF常量。212 231 x 247 252 } 021 306
:代表八字节长的校验和。
7. RDB参数配置
dir /var/lib/redis # RDB文件保存目录
dbfilename dump.rdb # RDB文件名(可自定义)
rdbcompression yes # 启用LZF算法压缩文件(节省空间,消耗CPU)
rdbchecksum yes # CRC64校验,确保数据完整性(增加10%性能开销)
stop-writes-on-bgsave-error yes # BGSAVE失败时停止写入,防止数据不一致
rdb-max-memory 2gb # 快照时最多使用2GB内存
rdb-child-priority 5 # 提升子进程优先级,加快快照速度
8. RDB优缺点
优点:
- 高性能:快照生成由子进程处理,主线程无阻塞。
- 恢复速度快:适合大规模数据恢复。
- 备份便捷:单个文件便于传输和灾备。
缺点:
- 数据丢失风险:两次快照间隔内的数据可能丢失。
- fork 性能问题:大数据集时
fork
操作可能阻塞主线程。 - 版本兼容性:不同 Redis 版本的 RDB 文件格式可能不兼容。
二、AOF持久化
1. AOF核心原理
AOF 通过 记录所有写操作命令 ,将每个写操作(如 SET
, HSET
, DEL
)以 Redis 协议格式追加到 appendonly.aof
文件中,服务器重启时重放命令恢复数据。
2. AOF同步策略
AOF 提供三种 appendfsync
策略:
always
:每次写操作立即同步到磁盘(数据安全性高,但性能差)。- 当appendfsync的值为always时,服务器在每个事件循环都要将aof_buf缓冲区中的所有内容写入到AOF文件,并且同步AOF文件,所以always的效率是appendfsync选项三个值当中最慢的一个,但从安全性来说,always也是最安全的,因为即使出现故障停机,AOF持久化也只会丢失一个事件循环中所产生的命令数据。
everysec
(默认):每秒同步一次(平衡性能与数据安全)。- 当appendfsync的值为everysec时,服务器在每个事件循环都要将aof_buf缓冲区中的所有内容写入到AOF文件,并且每隔一秒就要在子线程中对AOF文件进行一次同步。从效率上来讲,everysec模式足够快,并且就算出现故障停机,数据库也只丢失一秒钟的命令数据。
no
:由操作系统决定同步时机(性能高,但数据丢失风险最大)。- 当appendfsync的值为no时,服务器在每个事件循环都要将aof_buf缓冲区中的所有内容写入到AOF文件,至于何时对AOF文件进行同步,则由操作系统控制。因为处于no模式下的flushAppendOnlyFile调用无须执行同步操作,所以该模式下的AOF文件写入速度总是最快的,不过因为这种模式会在系统缓存中积累一段时间的写入数据,所以该模式的单次同步时长通常是三种模式中时间最长的。从平摊操作的角度来看,no模式和everysec模式的效率类似,当出现故障停机时,使用no模式的服务器将丢失上次同步AOF文件之后的所有写命令数据。
3. AOF文件生成
AOF文件生成详细步骤如下:
- 服务端将客户端请求的写命令append追加到aof_buf缓冲区末尾。
- aof_buf缓冲区会根据appendfsync配置的持久化策略将写命令同步到磁盘的AOF文件。
- 当AOF文件大小超过重写策略或手动重写时,会对AOF文件rewrite重写,压缩AOF文件容量。
- 当Redis服务重启时,会加载AOF文件内容在伪客户端执行,恢复数据。
4. AOF文件加载
-
当 AOF 开启时,Redis 启动时会优先载入 AOF 文件来恢复数据,即使AOF文件不存在也不会加载RDB文件。只有当 AOF 关闭时,才会载入 RDB 文件恢复数据。
-
Redis 载入 AOF 文件时,会对 AOF 文件进行校验,如果文件损坏,则日志中会打印错误,Redis 启动失败。但如果是 AOF 文件结尾不完整(机器突然宕机等容易导致文件尾部不完整),且
aof-load-truncated
参数开启,则日志中会输出警告,Redis 忽略掉 AOF 文件的尾部,启动成功。aof-load-truncated
参数默认是开启的。 -
Redis 完成 AOF 文件校验后,执行载入 AOF 文件并还原数据库状态的详细步骤如下:
- 创建一个不带网络连接的伪客户端(fake client):因为Redis的命令只能在客户端上下文中执行,而载入AOF文件时所使用的命令直接来源于AOF文件而不是网络连接,所以服务器使用了一个没有网络连接的伪客户端来执行AOF文件保存的写命令,伪客户端执行命令的效果和带网络连接的客户端执行命令的效果完全一样。
- 从AOF文件中分析并读取出一条写命令。
- 使用伪客户端执行被读出的写命令。
- 一直执行步骤2和步骤3,直到AOF文件中的所有写命令都被处理完毕为止。
- AOF文件恢复完成。
5. AOF文件结构
假设我们在Redis执行了如下三条命令:
redis> SET msg hello
OK
redis> SADD fruits apple banana cherry
OK
redis> RPUSH numbers 128 256 512
OK
因为AOF文件存储的是与客户端指令一样的写命令,所以我们可以直接打开AOF文件查看内容:
*2\r\n$6\r\nSELECT\r\n$1\r\n0\r\n
*3\r\n$3\r\nSET\r\n$3\r\nmsg\r\n$5\r\nhello\r\n
*5\r\n$4\r\nSADD\r\n$6\r\nfruits\r\n$5\r\napple\r\n$6\r\nbanana\r\n$6\r\ncherry\r\n
*5\r\n$5\r\nRPUSH\r\n$7\r\nnumbers\r\n$3\r\n128\r\n$3\r\n256\r\n$3\r\n512\r\n
对比我们的执行的指令多了一条SELECT 0
命令,这个是因为Redis要连接指定数据库而加上的。AOF文件在还原数据的时候只需要在伪客户端逐行读取并执行,直到全部指令执行完成。
6. AOF文件重写
AOF 文件会随时间增长越来越大,使用AOF文件来进行数据还原所需的时间就越多。为了解决AOF文件体积过大的问题,Redis提供了AOF文件重写(rewrite)功能来压缩AOF文件体积。
6.1 AOF重写实现原理
AOF 文件重写通过读取Redis数据库现有的数据键值对,分析生成还原数据库所必须的写操作命令,再将写操作命令写入到新的AOF文件,最后将新AOF文件覆盖旧AOF文件,完成AOF文件重写。
AOF 文件重写其实就是扫描现有数据生成写操作语句,优化掉了旧AOF文件的中间过程的写操作指令,达到压缩AOF文件体积的目的。
6.2 AOF重写触发方式
- 手动触发:通过
BGREWRITEAOF
命令直接触发,适用于紧急情况或定期维护。 - 自动触发:自动触发由配置文件中的两个参数控制,需同时满足以下两个参数条件。
auto-aof-rewrite-min-size
:默认64MB,当AOF文件大小超过此值时,才可能触发重写。例如,设置为100MB,则AOF文件需≥100MB才可能触发重写。auto-aof-rewrite-min-size
:默认100,当前AOF文件大小是上次重写后大小的百分比增长。例如,设置为100时,当前文件需是上次的200%大小。
6.3 AOF文件重写流程
AOF文件重写会依据重写策略或手动触发执行。以BGREWRITEAOF
手动触发为例,以下是AOF文件重写的详细步骤:
- 执行
BGREWRITEAOF
命令触发文件重写,服务器主进程首先判断当前是否存在正在执行 bgsave/bgrewriteaof 的子进程,如果存在则 bgrewriteaof 命令直接返回,如果存在 bgsave 命令则等 bgsave 执行完成后再执行。 - 服务器主进程执行 fork 操作创建子进程,这个 fork 过程中主进程是阻塞的。 fork 完成后,主进程重新为客户端提供服务,子进程创建完成后开始执行AOF重写操作。
- 主进程接受客户端的写操作请求,将写操作同时追加到
aof_buf
和aof_rewirte_buf
两个缓冲区,保证旧AOF文件完整以及新AOF文件在生成期间的新数据写操作不会丢失。与此同时,子进程根据fork操作时的内存快照,按照命令合并规则写入到新的 AOF 文件。 - 1.子进程写完新的AOF文件后,向父进程发信号,通知主进程更新统计信息,具体可以通过 info persistence 查看。2.主进程把
aof_rewirte_buf
缓冲区的数据写入到新的AOF文件,这样就保证了新 AOF 文件所保存的数据库状态和服务器当前状态一致。 - 主进程使用新的AOF文件覆盖旧AOF文件,完成AOF重写。
7. AOF参数配置
# 启用AOF持久化
appendonly yes# 设置AOF文件名称和路径
appendfilename "appendonly.aof"
dir /var/lib/redis# 每秒同步一次(性能与安全的平衡)
appendfsync everysec# 自动重写配置:文件增长100%且≥64MB时触发
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb# 重写期间仍同步,确保数据一致
no-appendfsync-on-rewrite no# 允许加载损坏的AOF文件
aof-load-truncated yes
8. AOF优缺点
优点:
- 数据安全性高:
everysec
策略下最多丢失 1 秒数据。 - 可读性强:AOF 文件是文本格式,便于调试和恢复。
缺点:
- 文件体积大:相比 RDB,AOF 文件通常更大。
- 恢复速度慢:需逐条执行命令,大数据集恢复较慢。
- 性能开销:频繁的磁盘 I/O 可能影响性能。
三、RDB 与 AOF 对比
特性 | RDB | AOF |
---|---|---|
数据安全性 | 低(最多丢失最后一次快照后的数据) | 高(everysec 下最多丢失 1 秒数据) |
文件大小 | 小(二进制压缩格式) | 大(文本日志格式) |
恢复速度 | 快(直接加载快照) | 慢(需重放所有命令) |
写入性能 | 高(快照异步生成) | 低(依赖 appendfsync 配置) |
适用场景 | 备份、冷备、大数据集恢复 | 热备、实时数据保护 |
四、混合持久化(AOF + RDB)
1. 混合持久化的核心概念
混合持久化(Hybrid Persistence)是Redis 4.0+引入的一种持久化机制,结合了**RDB(快照)和AOF(追加日志)**的优点,旨在平衡数据安全性、恢复速度与存储效率。其核心思想是:
- RDB部分:记录某一时刻的内存快照(二进制格式,加载速度快)。
- AOF部分:记录快照之后的增量写命令(文本格式,数据完整性高)。
通过这种方式,混合持久化既利用了RDB的高效恢复特性,又保留了AOF的精细数据变更记录。
2. 混合持久化的工作原理
2.1 触发条件
混合持久化需通过AOF重写触发,因此其触发条件与AOF重写一致:
- 手动触发:执行
BGREWRITEAOF
命令。 - 自动触发:满足
auto-aof-rewrite-min-size
和auto-aof-rewrite-percentage
参数条件。
2.2 重写流程
当AOF重写被触发时,Redis执行以下步骤生成混合持久化文件:
- 创建临时文件:子进程生成一个临时文件(如
temp-rewriteaof.aof
)。 - 写入RDB快照:将当前内存数据以RDB格式写入临时文件的前半部分。
- 追加AOF增量命令:将重写期间的新写命令以AOF格式追加到临时文件的后半部分。
- 替换旧文件:通过
rename
原子操作将临时文件替换为新的AOF文件。
2.3 文件结构
混合持久化生成的AOF文件结构如下:
[RDB格式快照] + [AOF格式增量命令]
- RDB部分:包含重写时刻的完整内存数据,采用二进制压缩格式。
- AOF部分:记录RDB快照之后的所有写命令,采用文本协议格式。
2.4 重启加载流程
Redis启动时,按以下顺序加载混合持久化文件:
- 启动时检查 AOF 文件:
- Redis 判断 AOF 文件是否存在且开启了混合持久化。
- 加载 RDB 快照:
- 如果 AOF 文件以
REDIS
开头(RDB 格式标识),则加载 RDB 部分。
- 如果 AOF 文件以
- 重放 AOF 日志:
- 加载完 RDB 后,继续重放 AOF 日志中的增量操作。
- 完成恢复:
- 数据集恢复到持久化时的最新状态。
3. 混合持久化参数配置
# 启用AOF持久化
appendonly yes# 设置AOF文件名称和路径
appendfilename "appendonly.aof"
dir /var/lib/redis# 每秒同步一次(性能与安全的平衡)
appendfsync everysec# 自动重写配置:文件增长100%且≥64MB时触发
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb# 重写期间仍同步,确保数据一致
no-appendfsync-on-rewrite no# 允许加载损坏的AOF文件
aof-load-truncated yes# 启用混合持久化(Redis 4.0+)
aof-use-rdb-preamble yes
4. 混合持久化适用场景
- 对恢复速度要求高:如高并发服务,需快速重启恢复业务。
- 数据变更频繁:如社交、电商等需要实时写入的场景。
- 存储资源有限:希望减少AOF文件体积,降低磁盘占用。
- 兼容性要求:需同时支持RDB和AOF的特性(如备份、主从复制)。
5. 混合持久化的优缺点
优点:
- 快速恢复:RDB部分加载速度快,适合大数据集场景。
- 数据安全:AOF部分确保最后一次写入的数据不丢失。
- 存储优化:相比纯AOF,混合文件体积更小(RDB压缩 + 增量命令)。
- 兼容性:支持所有AOF功能(如
BGREWRITEAOF
、aof-load-truncated
)。
缺点:
- 文件可读性差:混合文件包含二进制RDB数据,无法直接编辑或查看。
- 版本限制:需Redis 4.0+,旧版本无法解析混合格式。
- 资源消耗:重写时需同时生成RDB和AOF数据,增加CPU和内存开销。
总结
Redis 的持久化机制是保障数据安全的关键。RDB 适合大规模数据恢复和冷备,AOF 提供更高的数据安全性,而 混合持久化 结合两者优势,是生产环境的推荐方案。