Redis 为什么需要持久化
RDB(Redis Database)
RDB 持久化以指定的时间间隔对数据集执行时间点快照(实际上就是记录某一个时间节点的内存数据)。
配置操作
Redis 7.4.2 默认 RDB 参数
|
|
默认情况下,Redis 会将数据集的快照保存在磁盘上一个名为 dump.rdb 的二进制文件中。 你也可以手动调用 SAVE 或 BGSAVE 命令。
- 执行 save 命令,就会在主线程生成 RDB 文件,由于和执行操作命令在同一个线程,所以如果写入 RDB 文件的时间太长,会阻塞主线程。线上禁止使用
- 执行了 bgsave 命令,会创建一个子进程来生成 RDB 文件,这样可以避免主线程的阻塞
执行 flushall/flushdb 命令也会产生 .rdb
文件,但是该文件中没有任何数据(因为他是在清盘之后才保存的)。在恢复数据时,一定要将服务和备份分机隔离(防止备份的 .rdb
文件被覆盖)。
触发时机
- 配置文件中默认的快照配置
- 手动 sava/bgsave 命令
- 执行 flushall/flushdb 命令
- 执行 shutdown 且没有设置 AOF 持久化
- 主从复制,主节点自动触发
优势
- 适合大规模的数据恢复
- 按照业务定时备份
- 对数据完整性和一致性要求不高
- RDB 文件在内存中加载速度比 AOF 快得多
劣势
- RDB 会丢失备份间隔时间内的所有数据,也就是说距离上一次备份期间内的所有数据都会被丢失
- RDB 是全量快照,每次执行都会把所有的数据记录到磁盘中,频繁的磁盘 I/O 可能影响服务器性能
- RDB 依赖主进程的 fork,在极端形况下内存占用会变为原先的 2 倍。
执行 RDB 快照时能否修改数据
可以,使用写时复制技术。
执行 bgsave 命令时,通过 fork() 创建子进程,因此父进程、子进程共享同一片内存数据(两份页表,一份物理内存)。只有当发生数据修改时,才会复制一份新的物理内存(注意此时子进程会将旧的物理内存写入 RDB 文件,而主进程会在新的物理内存中修改数据)。这样就可以避免主线程阻塞。
AOF(Append Only File)
AOF 持久性记录服务器收到的每个写入操作。 这些操作可以在服务器启动时再次重放,重建原始数据集。 命令记录格式与 Redis 协议本身相同。
配置操作
Redis 7.4.2 默认 AOF 参数
|
|
默认情况下,Redis 会将记录到的命令文件保存在磁盘上一个名为 appendonly.aof 的文件中。 Redis 6 之前只有一个 AOF 文件。Redis 7 之后有三个文件(合并在一个文件中):
- appendonly.aof.1.base.rdb:基础 AOF,最多只有一个,一般由子进程通过重写产生
- appendonly.aof.1.incr.aof:增量 AOF,可能存在多个,一般会在 AOF 重写时创建
- 历史 AOF,每次 AOF 重写完成时,之前的基础 AOF 和增量 AOF 都会编程历史 AOF。Redis 配置里没有,因为它会被自动删除
- appendonly.aof.manifest:跟踪管理 AOF 文件
工作流程
- Client 向 Redis 发送操作命令
- 这些命令首先进入 AOF 缓冲区保存(避免频繁的磁盘 I/O 操作)
- AOF 根据写回策略将缓冲区命令写入磁盘中
- 为了 AOF 文件的膨胀,会进行 AOF 重写压缩文件
- Redis 会在服务启动时加载磁盘中的 AOF 文件再次执行这些命令
执行优势
Redis 是先执行写操作命令后,才将该命令记录到 AOF 日志里的,这么做其实有两个好处。
- 避免额外的检查开销,因为能执行的命令必然是正确的命令
- 不会阻塞当前写操作命令的执行,因为只有写命令直接成功后才会记录
执行劣势
- 当 Redis 执行命令后,记录命令前,这个时刻 Redis 服务宕机,那么这个数据就会有丢失的风险
- 由于 Redis 执行命令和 AOF 写入这两个操作均是在一个线程中完成,那么可能导致下一个命令阻塞
写回策略
上图中第二步和第三步可以再细化为以下几步:
- Redis 执行完写命令后,将该命令追加到
server.aoof_buf
缓冲区中 - 通过调用系统函数
write()
,将缓冲区内数据写入到 AOF 文件中,此时数据并没有写入到硬盘,而是拷贝到了内核缓冲区page cache
- 内核缓冲区将数据写入硬盘
Redis 提供了三种写回策略
- Always
- 策略含义:每次写命令执行完后,将 AOF 写回硬盘
- 实际操作:每次写命令执行完之后立刻执行
fsync()
函数
- Everysec
- 策略含义:每次写命令执行完后,先将其写入 AOF 缓冲区,之后每隔一秒将缓冲区写入磁盘
- 实际操作:每次写命令执行完之后,会创建一个异步进程执行
fsync()
函数
- No
- 策略含义:每次写命令执行完后,先将其写入 AOF 缓冲区,之后将缓冲区写入磁盘(由操作系统决定间隔时间)
- 实际操作:永远不执行
fsync()
函数
小结
写回策略 | 写回时机 | 优点 | 缺点 |
---|---|---|---|
Always | 同步写回 | 可靠性高 | 性能开销大 |
Everysec | 每秒写回 | 性能、可靠性均衡 | 宕机丢失 1 秒内数据 |
No | 由操作系统决定 | 性能高 | 宕机丢失数据多 |
重写机制
当 AOF 文件的大小(默认64MB)超过所设定的阈值后,Redis 启动 重写机制。由于重写操作十分耗时,所以该过程是由子进程 bgrewriteaof
完成的,有以下两点好处
- 重写期间避免阻塞主进程
- 子进程带有主进程的数据副本,如果父子进程任何一方修改内存会触发写时复制机制,确保数据安全。
触发重写机制后,主进程就会创建重写的 AOF 子进程,此时父子进程共享物理内存,重写子进程只会对该内存读取,重写 AOF 子进程会读取数据库里的所有数据,并逐一把内存数据的键值对转换为一条命令,再将命令记录到重回日志(新的 AOF 文件)。
子进程完成 AOF 重写工作后,会向主进程发送一条异步信号。主进程收到信号后首先将 AOF 缓冲区中所有内容追加到新的 AOF 文件中,然后将新的 AOF 文件改名,覆盖掉旧的 AOF 文件。