持久化
必要性
Redis 主要是一个基于内存的数据库,这意味着所有数据默认存储在服务器的RAM中。RAM的读写速度非常快,这是Redis高性能的关键原因之一。但是,如果Redis服务器因为断电、崩溃或其他原因重启,内存中的所有数据都会丢失。为了解决这个问题,Redis提供了持久化机制。
RDB持久化
什么是RDB
RDB(Redis DataBase)持久化通过在指定的时间间隔内创建数据集的时间点快照 (point-in-time snapshot) 来工作。它会生成一个经过压缩的二进制文件(默认为 dump.rdb
),其中包含了在特定时刻Redis数据库中的所有数据。
RDB如何工作
当满足RDB持久化的触发条件时(手动或自动),Redis会执行以下操作:
- Fork子进程:Redis 主进程会
fork()
一个子进程。fork()
操作会创建一个与父进程几乎完全相同的副本。- Copy-On-Write (COW写时复制):
fork()
之后,父子进程共享相同的内存页。父进程继续处理客户端请求。如果父进程需要修改某个内存页,操作系统会将该页复制一份,父进程在新复制的页上进行修改,而子进程仍然读取旧的、未修改的页。这确保了子进程看到的是fork()
时刻的数据快照,并且父进程的性能影响降到最低。
- Copy-On-Write (COW写时复制):
- 子进程写RDB文件: 子进程负责将内存中的数据快照写入到一个临时的RDB文件中。
- 替换旧文件: 当子进程完成RDB文件的写入后,它会用这个新的临时文件替换掉旧的
dump.rdb
文件。 - 子进程退出: 完成替换后,子进程退出。
如何触发RDB
SAVE
命令 (手动,同步阻塞):
- 客户端执行
SAVE
命令。 - Redis主进程亲自执行RDB保存过程,期间会阻塞所有其他客户端请求,直到RDB文件创建完毕。
- 生产环境严禁使用,因为它会导致服务在保存期间不可用。
BGSAVE
命令 (手动,异步非阻塞):
- 客户端执行
BGSAVE
命令。 - Redis主进程
fork()
一个子进程,由子进程负责RDB的保存。 - 主进程继续处理客户端请求,不会阻塞。这是推荐的手动触发方式。
- 配置文件自动触发 (redis.conf): 可以在
redis.conf
文件中通过save
配置项来设定自动触发BGSAVE
的条件。格式为:save <seconds> <changes>
可以配置多条save
规则,满足任意一条规则即会触发。
-
例如,默认配置通常类似:
save 900 1 # 900秒(15分钟)内至少有1个key被修改 save 300 10 # 300秒(5分钟)内至少有10个key被修改 save 60 10000 # 60秒(1分钟)内至少有10000个key被修改
-
如果想禁用自动RDB,可以将所有
save
规则注释掉,或者配置save ""
。
-
执行
FLUSHALL
或FLUSHDB
命令时: 如果没有禁用RDB,执行清空数据库的命令也会触发RDB保存一个空的dump.rdb
文件。 -
主从复制时: 当从服务器连接到主服务器并进行全量同步时,主服务器会自动执行
BGSAVE
生成RDB文件发送给从服务器。
RDB的优缺点
优点:
-
性能影响相对较小(对主进程):
BGSAVE
使用子进程进行持久化,主进程可以继续服务客户端请求,对性能影响较小(除了fork()
操作本身可能带来的短暂延迟,尤其是在大数据集下)。 -
恢复速度快: RDB文件是经过压缩的二进制文件,包含了某个时间点上的完整数据快照。因此,在数据恢复时,Redis只需要直接读取和解析这个文件即可,通常比AOF的恢复速度快(尤其是在AOF文件很大且没有使用RDB序言的情况下)。
-
文件紧凑: RDB文件经过压缩,占用的磁盘空间通常比AOF文件小。
-
适合备份和灾难恢复: 单个紧凑的RDB文件非常适合进行全量备份,例如每天备份一次,然后可以轻松地将其传输到远程数据中心或存储服务。
缺点:
-
数据丢失风险较高: RDB是按时间间隔或修改次数进行快照的。如果在两次快照之间Redis服务器发生故障,那么从上一次快照到故障发生这段时间内的所有数据修改都会丢失。这个丢失窗口的大小取决于你的
save
配置。 -
fork()
操作的成本: 虽然有COW机制,但在数据集非常大、内存使用率很高的情况下,fork()
操作本身可能会消耗较多的CPU时间和内存资源,甚至可能导致服务短暂卡顿。 -
数据一致性: RDB文件保存的是某一时刻的快照,如果业务对数据一致性有非常严格的要求(例如,要求能恢复到故障前一秒的状态),RDB可能无法满足。
Redis.conf中与RDB相关的设置
-
save <seconds> <changes>
: 自动触发BGSAVE的条件。 -
dbfilename dump.rdb
: RDB文件的名称。 -
dir ./
: RDB文件和AOF文件存放的目录。 -
stop-writes-on-bgsave-error yes
: 当BGSAVE(后台保存)操作出错时,Redis是否停止接受写操作。设置为yes
可以及时发现磁盘、权限等问题,防止数据无法持久化。 -
rdbcompression yes
: RDB文件是否进行压缩。默认为yes
,可以显著减小文件大小,但在保存和加载时会消耗一些CPU。 -
rdbchecksum yes
: 在RDB文件的末尾是否加入CRC64校验和。默认为yes
。开启后,加载RDB文件时会进行校验,可以提高数据完整性,但保存和加载时会增加约10%的性能消耗。
AOF持久化
什么是AOF
AOF(Append Only File)持久化通过记录服务器接收到的每一个写操作命令(以Redis协议的格式)到一个只追加的文件(默认为 appendonly.aof
)中来实现。当Redis重启时,它会重新执行AOF文件中保存的所有写命令,从而恢复数据集。
AOF如何工作
-
命令追加 (Append): 当一个写命令被客户端发送到Redis并成功执行后,该命令会被追加到AOF文件的末尾。
-
文件同步 (Sync): AOF机制的核心在于如何以及何时将AOF缓冲区中的数据真正写入到磁盘上的AOF文件中。这由
appendfsync
配置项控制。
appendfsync always
: 每个写命令执行完毕后,都立即调用fsync()
将AOF缓冲区的数据同步到磁盘。这是最安全的方式,能保证数据基本不丢失(最多丢失一个事件循环中的命令),但性能开销极大,严重影响Redis的QPS。appendfsync everysec
(默认值): 每秒调用一次fsync()
。这种方式在性能和数据安全性之间取得了很好的平衡。即使发生故障,最多也只会丢失过去1秒内的数据。appendfsync no
: 不主动调用fsync()
,完全依赖操作系统的文件同步机制。数据将由操作系统决定何时刷新到磁盘。速度最快,但数据丢失风险最高。
AOF重写(Rewrite)
随着时间的推移,AOF文件会因为记录了大量的写命令而变得越来越大。例如,对一个计数器执行100次 INCR
,AOF文件中会记录100条 INCR
命令,而实际上最终只需要一条 SET
命令就能表示这个计数器的当前值。
为了解决AOF文件过大的问题,Redis提供了AOF重写机制:
- 目的: 创建一个新的、更紧凑的AOF文件,这个新文件包含恢复当前数据集所需的最少命令集。
- 如何工作:
- Redis主进程会
fork()
一个子进程(与BGSAVE类似,使用COW)。 - 子进程读取当前内存中的数据库状态,并将其转换成一系列最简的写命令(例如,多个
LPUSH
可能被一个包含所有元素的LPUSH
命令替代,或者一个计数器的多次INCR
被一个SET
命令替代)。 - 子进程将这些命令写入到一个新的临时AOF文件中。
- 在子进程重写期间,父进程接收到的新的写命令仍然会被追加到旧的AOF文件的末尾,同时也会被缓存到一块内存区域中(AOF重写缓冲区)。
- 当子进程完成新AOF文件的写入后,父进程会将AOF重写缓冲区中的增量命令追加到新的AOF文件的末尾,以确保新旧数据一致。
- 最后,Redis原子地用新的AOF文件替换旧的AOF文件,并开始向新的AOF文件追加后续命令。
- Redis主进程会
- 触发方式:
BGREWRITEAOF
命令 (手动): 执行此命令会异步触发AOF重写。- 配置文件自动触发 (redis.conf):
auto-aof-rewrite-percentage 100
: 当AOF文件的当前大小比上一次重写后的大小增长了100%(即翻倍)时,触发重写。auto-aof-rewrite-min-size 64mb
: 触发AOF重写的最小AOF文件大小。只有当AOF文件大小同时满足这两个条件时才会自动重写。
AOF文件的加载
当Redis启动时,如果AOF持久化被启用(appendonly yes
),Redis会优先加载AOF文件来恢复数据,忽略RDB文件(除非AOF文件不存在或损坏,或者配置了特殊情况)。Redis会逐条执行AOF文件中的命令。
AOF RDB序言 (RDB Preamble - Redis 4.0+): 从Redis 4.0开始,AOF文件在重写时可以在文件开头包含一个RDB格式的快照,后面再跟着增量的AOF记录。这个特性(通过 aof-use-rdb-preamble yes
开启,在Redis 7.0+中默认为yes
)极大地提高了AOF恢复的速度,因为它首先像加载RDB一样快速加载基础数据,然后只应用RDB快照之后的增量AOF命令。
AOF的优缺点
优点:
-
数据更完整,丢失风险更低: 根据
appendfsync
策略,可以配置为每秒同步一次甚至每个命令同步一次,数据丢失的窗口非常小。everysec
策略下最多丢失1秒的数据。 -
文件可读性高(部分): AOF文件以Redis协议格式存储命令,除了RDB序言部分,大部分是文本格式,理论上可以理解和编辑(但不推荐直接编辑损坏的AOF文件,有专门的修复工具)。
-
写入操作是追加模式: 如果AOF文件因为磁盘满或其他原因写入不完整,可以使用
redis-check-aof
工具轻松修复,它会移除末尾不完整的命令。
缺点:
-
文件体积通常比RDB大: 即使经过重写,AOF文件通常也比相同数据集的RDB文件要大,因为它存储的是操作命令序列。
-
恢复速度可能比RDB慢: 需要逐条执行命令来恢复数据。不过,有了RDB序言后,这个缺点在很大程度上得到了缓解。
-
性能开销可能略高于RDB: 取决于
appendfsync
策略。always
策略对性能影响很大,everysec
策略通常也能接受。
Redis.conf中与AOF相关的设置
-
appendonly no
: 是否启用AOF持久化。默认为no
,需要改为yes
来启用。 -
appendfilename "appendonly.aof"
: AOF文件的名称。 -
appendfsync everysec
: AOF同步策略,可选always
,everysec
,no
。 -
no-appendfsync-on-rewrite no
: 在AOF重写期间,主进程是否对追加到旧AOF文件的命令执行fsync
。通常设置为yes
(或默认让其为no
但依赖子进程的fsync)可以避免主进程阻塞。设置为no
(即在重写时主进程的append
操作也会fsync
)会更安全但影响性能。 -
auto-aof-rewrite-percentage 100
: 触发自动AOF重写的增长百分比。 -
auto-aof-rewrite-min-size 64mb
: 触发自动AOF重写的最小文件大小。 -
aof-load-truncated yes
: 如果AOF文件在末尾损坏(例如由于系统崩溃导致写入不完整),Redis启动时是否加载这个不完整的AOF文件。加载后会打印日志通知用户。 -
aof-use-rdb-preamble yes
: (Redis 4.0+) 在AOF重写时,是否在AOF文件头部使用RDB格式的序言。默认为yes
(Redis 7.0+),建议开启以加速恢复。
评论区
请登录后发表评论