一、RDB 持久化
可以将 redis 在内存中的数据库状态保存到磁盘里面,避免数据因为服务器进程退出而丢失。
RDB 持久化既可以手动执行,也可以根据服务器配置选项定期执行,该功能可以将某个时间点上的数据库状态保存到一个 RDB 文件中。
RDB 持久化功能所生成的 RDB 文件是一个经过压缩的二进制文件。有 SAVE 和 BGSAVE 命令
- SAVE 命令会阻塞 Redis 服务器进程,直到 RDB 文件创建完毕为止,在服务器进程阻塞期间,服务器不能处理任何命令请求
- BGSAVE 命令会派生出一个子进程,然后由子进程负责创建RDB 文件,服务器进程继续处理客户端命令
- 当服务器执行 BGSAVE 时,客户端发送的SAVE、BGSAVE 命令会被服务器拒绝;BGREWRITEAOF 命令会被延迟到 BGSAVE 命令执行完毕之后执行。如果 BGREWRITEAOF 命令正在执行,客户端发送的 BGSAVE 命令会被拒绝。
- BGREWRITEAOF 和 BGSAVE 两个命令都是由子进程执行,虽然没有冲突,但是两个子进程都同时执行大量的磁盘写入操作,影响性能。
RDB 文件的载入工作是在服务器启动时自动执行的,且为阻塞的。因为AOF文件的更新频率比RDB文件的更新频率高,所以服务器如果开启了AOF持久化功能,优先使用AOF文件还原数据库状态。只有在AOF持久化功能处于关闭状态时,服务器才会使用RDB文件还原数据库状态
1. 保存条件
redis 通过配置定时去 BGSAVE。Redis 服务器会默认 100ms 执行一次检查 save 选项设置的保存条件是否满足。如果任意一个条件被满足,就执行 BGSAVE 命令。
- 距离上次执行保存的时间超过条件所设置的时间
- 数据库状态的修改次数超过条件所设置的次数
1 | save 900 1 # 服务器在 900 秒之内,对数据库进行了至少1次修改,下同 |
- dirty 计数器记录距离上一次成功执行 SAVE 命令或者 BGSAVE 命令之后,服务器对数据库状态(服务器中的所有数据库)进行了多少次修改(增删改)
- lastsave 属性是一个 unix 时间戳,记录了服务器上一次成功执行 SAVE / BGSAVE 命令的时间
2. RDB 文件结构
1 | | REDIS | db_version | databases | EOF | check_sum | |
- REDIS 部分是固定的 5 个字节,保存“REDIS”五个字符
- db_version 长度为 4 个字节,是一个字符串表示的整数,比如“0006”
- databases 部分包含着零个或者任意多个数据库,以及各个数据库中的键值对数据
- EOF 常量的长度为 1 个字节,标志着 RDB 文件正文内容的结束
- check_sum 是一个 8 字节长的无符号整数,保存着校验和,这个校验和是程序通过对前四个部分的内容进行计算得出的。在载入RDB文件时,会将载入数据所计算的校验和与 check_sum 所记录的校验和进行对比,判断是否出错或者损坏。
3. 分析 RDB 文件
1 | -c:以 ASCII 编码的方式打印输入文件 |
4. 优缺点
优点:
- 方便备份,可以方便的将一个 RDB 文件移动到其他的存储介质上
- RDB 在恢复大数据集时速度比 AOF 的恢复速度要快
- RDB 可以最大程序上保证 redis 的性能,父进程在保存 RDB 文件时唯一要做的就是 fork 出一个子进程,然后这个子进程就会处理接下来的所有保存工作,父进程无须执行任何磁盘 IO 操作
缺点:
- RDB 文件需要保存整个数据集的状态,我们可以控制发生 RDB 的时间间隔,但是这个时间间隔内发生的故障停机,就会导致数据丢失
- 每次保存 RDB 文件的时候,redis 都要 fork 出一个子进程来进行持久化操作,在数据集比较庞大时,fork 可能会非常耗时,造成服务器在 fork 的时间段无法处理客户端请求。
二、AOF 持久化
AOF 持久化通过保存 Redis 服务器所执行的 “写命令” 来记录数据库状态的。在 AOF 文件中,redis 服务器会自动添加 SELECT 命令用于指定某个数据库。AOF 功能的实现可以分为:命令追加、文件写入、文件同步三个步骤。
命令追加
服务器执行完一个写命令之后,会以一定的协议格式将被执行的写命令追加到名为 aof_buf 的 AOF 缓冲区的末尾
AOF 文件的写入和同步
Redis 服务器进程是一个事件循环,服务器每次结束一个事件循环之前,都会考虑是否需要将 aof_buf 缓冲区的内容写入和保存到 AOF 文件里面。根据不同的配置(appendfsync 值)产生不同的持久化行为。
Appendfsync 选项的值 行为 always 将 aof_buf 缓冲区中所有内容写入并同步(fsync)到AOF文件 everysec 将 aof_buf 缓冲区中所有内容写入到AOF 文件,如果上次同步AOF文件的时间距离现在超过一秒钟,那么再次对AOF文件进行同步,并且这个同步操作是由一个线程专门负责执行的 no 将aof_buf 缓冲区中的所有内容写入到AOF文件,但并不对AOF文件进行同步,何时同步由操作系统决定 注意:现代操作系统,当调用 write 函数写数据到文件时,操作系统通常将写入数据暂时保存在一个内存缓冲区里面,等到缓冲区的空间被填满、或者超过了指定的时限之后,才真正的将缓冲区中数据写入到磁盘。操作系统提供 fsync 完成同步操作。
这三种方式的效率和安全性分析:
- Appendfsync 为 always 时,因为每个事件循环都要同步,所以效率最慢。但是这种方式安全性较高,因为即使出现故障停机,AOF 持久化也只会丢失一个事件循环中所产生的命令数据。
- Appendfsync 为 everysec 时,因为一秒同步一次,所以效率还好。并且就算出现故障停机,数据库也只丢失一秒钟的命令数据。
- Appendfsync 为 no 时,因为无需手动执行同步操作,因此该模式下的 AOF 文件写入速度总是最快的;不过可能丢失的数据量比较多。
AOF 文件的载入与数据还原:AOF 文件包含了重建数据库状态所需的所有写命令,因此只需要创建一个不带网络的伪客户端(因为 redis 的命令只能在客户端上下文中执行,而载入 AOF 文件时所使用的命令直接来源于 AOF 文件而不是网络连接),通过这个客户端,读入并重新执行一遍 AOF 文件中保存的写命令,就可以还原服务器关闭之前的数据库状态。
1. AOF 重写
为什么要进行 AOF 重写:
- 随着 redis 服务器运行时间越长,AOF 文件中的内容会越来越多,可能会占用过多的磁盘空间;并且 AOF 文件体积越大,使用 AOF 文件来进行数据还原所需的时间就越多。
- 为了解决 AOF 文件体积膨胀的问题,redis 会进行 AOF 文件重写。也就是会创建一个新的 AOF 文件来替代现有的 AOF 文件,新旧两个 AOF 文件所保存的数据库状态相同,但新 AOF 文件不会包含任何浪费空间的冗余命令,所以新 AOF 文件的体积通常会比旧 AOF 文件的体积要小得多。
AOF 文件重写的实现:
遍历所有数据库的所有键,忽略过期的键,根据键的类型获取到键包含的所有元素,组装构造 “写入语句”。然后写入新的 aof 文件。因为新的 AOF 文件只包含还原当前数据库状态所必须的命令,所以新 AOF 文件不会浪费任何硬盘空间。
需要注意的一个细节,在构造 “写入语句” 时,为了避免在执行命令时造成客户端输入缓冲区溢出,重写程序在处理列表、哈希表、集合、有序集合这四种可能会带有多个元素的键时,会先检查键所包含的元素数量,如果元素数量超过了一个阈值(一般为 64),那么重写程序将使用多条命令来记录键的值,而不单单使用一条命令。
命令:BGREWRITEAOF 进行 AOF 后台重写
Redis 会将 AOF 重写程序放到子进程执行。这样做达到两个目的:
- 目的:子进程进行 AOF 重写期间,服务器进程可以继续处理客户端命令请求
- 目的:子进程带有服务器进程的数据副本,使用子进程,可以在不使用锁的情况下,保证数据的安全性
产生问题:子进程在进行 AOF 重写期间,主服务器进程还需要继续处理客户端写命令请求,导致当前服务器的数据库状态和AOF文件所保存的数据库状态不一致。如何解决呢?
- Redis 服务器设置了一个 AOF 重写缓冲区,这个缓冲区在服务器创建子进程之后开始使用,当 Redis 服务器执行完一个写命令之后,他会同时将这个写命令发送给 AOF 缓冲区和 AOF 重写缓冲区。
- 这样可以保证,AOF 缓冲区的内容会定期被写入和同步到 AOF 文件,对现有 AOF 文件的处理工作会照常进行。并且从创建子进程开始,服务器执行的所有写命令都会被记录到 AOF 重写缓冲区中。
当子进程完成 AOF 重写工作之后,它会向父进程发送一个信号,父进程收到信号后,会调用一个信号处理函数,执行如下操作:
- 将 AOF 重写缓冲区中的所有内容写入新的 AOF 文件中,这时新 AOF 文件所保存的数据库状态将和服务器当前的数据库状态一致
- 对新的AOF 文件进行改名,原子的覆盖现有的 AOF 文件,完成新旧两个 AOF 文件的替换
这个信号处理函数执行完毕后,父进程就可以继续像往常一样接受命令请求了。
那么在整个 AOF 后台重写过程中,只有信号处理函数执行时会对服务器进程(父进程)造成阻塞,这将 AOF 重写对服务器性能造成的影响降到了最低。
2. 优缺点
优点:
- 可以设置不同的 fsync 刷盘策略,AOF 默认的策略是每秒 fsync 一次,这种配置下,redis 仍然可以保持良好的性能,即使发生故障停机,也只会丢失一秒钟的数据(fsync 会在后台线程执行,主线程可以继续处理客户端命令)
- AOF 文件是一个只进行追加操作的日志文件,如果写入不完整的命令(比如写入时磁盘已满,写入中途停机等),redis 也提供了工具修复。当 AOF 文件体积较大时,会自动在后台对 AOF 进行重写,整个重写过程是绝对安全的。
- AOF 存储的以 redis 协议格式的所有写入操作,因此文件内容的可读性比较高。比如:如果不小心执行了 flushall 命令,但只要 AOF 文件未被重写,只要停止服务器,移除 AOF 文件末尾的 FlushAll 命令,并重启 Redis,就可以恢复数据集到 FlushAll 执行之前的状态。
缺点:
- 对于相同的数据集来说,AOF 文件的体积通常要大于 RDB 文件的体积
- 根据所使用的 fsync 策略,AOF 的速度可能会慢于 RDB 。 在一般情况下, 每秒 fsync 的性能依然非常高, 而关闭 fsync 可以让 AOF 的速度和 RDB 一样快
三、使用场景
- 数据恢复:redis 服务异常,AOF 比 RDB 更利于数据恢复。AOF 默认每秒将数据量追加到文件末尾存盘一次,RDB 是一个时间点的数据快照,时间跨度比较大。
- 数据备份:RDB 是 redis 内存数据快照,速度快,体积小。更适合数据备份存储
- redis 服务启动速度:redis 启动加载 RDB 文件比 AOF 快。因为 AOF 文件可能有冗余命令,RDB 是数据集合
- 持久化速度:AOF 默认每秒存盘和 RDB 持久化是异步存储,基本不影响主线程主逻辑功能。如果 AOF 采样写命令实时存盘,可能会严重影响 redis 服务性能
- 集群节点间全量同步:需要拷贝服务进程的内存数据,根据 RDB 持久化特点:速度快、体积小。显然 RDB 更适合于集群间传输数据