剖析Redis的持久化机制
1. Redis持久化的原因
总所周知,Redis以比MySQL更加高效而出名,其最大的武器就是数据内存化,而持久化既是MySQL的武器也是其弱点,毕竟持久化就要设计磁盘IO,磁盘IO的效率可比内存低得多,可是为什么Redis却也要去做持久化呢?
虽然内存的效率远比磁盘IO要高,但是内存中的数据是 易失性 的,如果 Redis 进程崩溃或服务器断电,内存中的数据将会丢失,因此,Redis为了解决这个问题,提出了持久化机制。
2. Redis的持久化机制
Redis总共有两个持久化策略:
- RDB(Redis Database)
- AOF(Append-Only File)
2.1 RDB机制
RDB 通过生成内存数据的快照,将某一时刻的数据库状态保存到磁盘上的 RDB 文件中,RDB文件以压缩后的二进制数据进行存储。RDB的触发机制有两种,一个是手动触发,一个是自动触发。RDB文件一般保存在/var/lib/redis下,当redis服务器启动时,会自动将rdb文件读进内存中。
手动触发
RDB的手动触发通过两个命令实现,一个是save,另一个是bgsave,save相当于一个Redis主线程实现的业务,如果数据量很大,一旦执行,就会阻塞其他任务,作用和keys *一个级别,这样显然是不推荐使用的。而bgsave(background save),顾名思义,后台保存,并不像save一样由主线程执行,从而卡住其他任务,而是由父进程fork出一个子进程,由该子进程去进行保存,当子进程保存完毕后,再发送信号通知父进程。
bgsave保存内存数据不是直接创建rdb文件或者清空rdb文件再写入,而是先创建一个临时文件(通常为temp-.rdb),pid是子进程的pid,当持久化完成后,再将旧的rdb文件删除并将临时文件改成旧的rdb文件名称,一般该名称为dump.rdb,可以在/etc/redis/redis.conf中修改。
但是有人可能要问,如果Redis里的数据很多,你单独创建一个子进程,又要将子进程的数据从父进程上拷贝一份,难道不会浪费内存吗,万一装不下了怎么办?
这就不得不谈到Linux下的一个重要优化机制——COW(写时拷贝),逻辑上,刚创建的子进程和父进程的数据是独立的,但是实际上,子进程和父进程通过页表映射到的物理内存是同一份,那当然数据也是,只有双方有一方修改了某一个内存页,操作系统才会将子进程对修改的内存页进行重新映射,这才指向不同的物理内存,而子进程只是用来将内存数据拷贝到rdb文件,不会进行修改物理内存,因此除非父进程大幅度的修改数据,否则不会占用太多内存空间。不过也因此,如果父进程在子进程fork以后修改的数据,子进程是无法知道的,就可能出现子进程快照的很多数据,父进程已经修改或者删除了。
自动触发
除了我们手动调用命令保存rdb文件,redis服务器也有特定的策略来进行自动快照保存,但是保存的方式是bgsave而不是save。自动触发条件主要是两种方式:
- 一定时间内,最少修改了一定次数,则形成一个快照。
- redis服务器正常退出时,形成一次快照。
RDB 自动存储的触发条件通过 save
配置项来定义。在 Redis 配置文件(redis.conf
)中,可以设置多个 save
规则,Redis 的默认配置一般包含三条 save
规则。格式如下:
save <seconds> <changes>
<seconds>
:时间间隔(秒)。<changes>
:在指定时间间隔内,至少有<changes>
个 key 被修改。
Redis 会检查所有 save
规则,如果满足其中任意一条规则,就会触发 BGSAVE
操作。
禁用rdb自动存储:
save ""
2.2 AOF机制
Redis 的 AOF(Append-Only File) 是一种 日志式持久化机制,用于将每个写操作追加到 AOF 文件中。通过重放 AOF 文件,Redis 可以在重启时恢复数据。AOF文件是一个文本格式文件。
AOF 的工作流程
- 写入操作:
- Redis 将每个写操作追加到 AOF 缓冲区(
aof_buf
)中。
- 刷新到磁盘:
- 根据
appendfsync
配置,Redis 会将 AOF 缓冲区中的数据刷新到磁盘上的 AOF 文件中(此操作由Redis父进程完成)。
- 重放 AOF 文件:
- 在 Redis 重启时,会重放 AOF 文件中的写操作,恢复数据。
AOF相关配置(redis.conf中):
-
appendonly
:是否启用 AOF 持久化。默认值为no。 -
appendfilename
:指定 AOF 文件的名称。默认值为appendonly.aof。 -
appendfsync
:定义 AOF 文件的刷新策略。
可选值:
always
:每次写操作后立即刷新到磁盘,数据安全性最高,但性能最差。everysec
:每秒刷新一次到磁盘,性能和数据安全性平衡。no
:依赖操作系统的刷新机制,性能最好,但数据安全性最低。
默认是everysec。
AOF的重写机制
由于Redis随时可能更改数据,那么随着操作的增加,可能存在于AOF文件中的写入操作就变得冗余起来,因此AOF为此提供了重写机制,即当AOF文件达到一定程度的大小时,Redis服务器会重新生成一份aof文件,父进程 fork 一个子进程,子进程负责根据父进程的快照状态生成新的 临时AOF 文件,temp-rewriteaof-.aof,只有当写完之后,才会删除原来的aof文件并rename。如果fork之后,形成新的aof文件之前,此时父进程又有操作修改,那么会额外开辟一段缓存区aod_rewrite_buf用于写入fork之后的写入操作,当子进程完成写入后,发送信号通知父进程,父进程才会将aod_rewrite_buf中的数据写入到新AOF文件中,但同时父进程也会写入到aod_buf中,这是为了保证AOF文件的原子性(即当Redis数据库不正常退出,数据未来得及保存,也可以根据原来的AOF文件进行恢复)。
触发条件:
- AOF 文件大小比上一次重写后的文件大小增长了
auto-aof-rewrite-percentage
指定的百分比。 - AOF 文件大小大于
auto-aof-rewrite-min-size
指定的最小大小。
Redis的持久化选择
当Redis服务器启动时,会先查看AOF是否启用,如果启用则查看是否有AOF文件,如果有则进行恢复,没有就不操作,而RDB即使启用且RDB文件存在,Redis服务器也不会读取,除非将AOF禁用。
2.3 RDB和AOF的优缺点
1. RDB(Redis Database)
优点
- 文件紧凑:RDB 文件是二进制格式的,文件体积较小,适合备份和恢复。
- 恢复速度快:RDB 文件包含某一时刻的完整数据,恢复速度较快。
- 适合大规模数据:RDB 文件生成过程中,主进程可以继续处理客户端请求,适合大规模数据集。
- 性能开销较低:RDB 是快照机制,生成快照的频率较低,性能开销相对较小。
缺点
- 数据丢失风险:RDB 是快照机制,如果 Redis 在两次快照之间崩溃,可能会丢失部分数据。
- 生成快照时性能影响:在生成 RDB 文件时,fork 操作可能会占用较多的内存和 CPU 资源。
- 不适合实时持久化:RDB 是定期生成快照,无法实现实时持久化。
适用场景
- 数据备份:适合用于数据备份,可以将 RDB 文件复制到其他服务器或存储设备。
- 灾难恢复:在 Redis 崩溃或服务器故障后,可以通过 RDB 文件快速恢复数据。
- 大规模数据:对于大规模数据集,RDB 文件的恢复速度较快。
2. AOF(Append-Only File)
优点
- 数据安全性高:AOF 文件记录了每个写操作,数据丢失的风险较低。
- 支持实时持久化:通过
appendfsync always
配置,可以实现实时持久化。 - 可读性强:AOF 文件是文本格式的,文件内容是可读的。
- 灵活性高:可以通过重写 AOF 文件来优化文件大小。
缺点
- 文件体积较大:AOF 文件记录了每个写操作,文件体积较大。
- 恢复速度较慢:在数据集较大时,重放 AOF 文件的恢复速度较慢。
- 性能开销较高:在高写入负载下,AOF 可能会影响 Redis 的性能。
适用场景
- 对数据安全性要求高的场景:例如金融、电商等。
- 需要实时持久化的场景:例如实时数据采集、日志记录等。
- 需要灵活持久化的场景:例如需要频繁调整持久化策略的场景。
3. 混合持久化
Redis 的 混合持久化 是一种结合了 RDB(Redis Database) 和 AOF(Append-Only File) 两种持久化机制的方式。它通过在 AOF 文件中嵌入 RDB 数据,既保留了 AOF 的 实时持久化 能力,又利用了 RDB 的 高效恢复 特性。
原理
在混合持久化模式下,Redis 会生成一个 混合格式的 AOF 文件,文件内容分为两部分:
- RDB 数据:文件的开头是一个完整的 RDB 快照,用于快速恢复数据。
- AOF 增量日志:RDB 数据之后是 AOF 格式的增量日志,用于记录 RDB 快照之后的写操作。
触发条件
混合持久化是通过 AOF 重写 触发的。在 AOF 重写时,Redis 会生成一个混合格式的 AOF 文件:
- 生成 RDB 数据:子进程根据当前数据库状态生成一个 RDB 快照,并将其写入新的 AOF 文件的开头。
- 追加 AOF 增量日志:在 RDB 数据之后,子进程会将重写期间的写操作以 AOF 格式追加到文件中。
要启用混合持久化,需要在 Redis 配置文件(redis.conf
)中设置以下选项:
bash
aof-use-rdb-preamble yes