Redis持久化机制
- [1. RDB](#1. RDB)
-
- [1.1 实现方式](#1.1 实现方式)
- [1.2 实现原理](#1.2 实现原理)
- [2. AOF](#2. AOF)
-
- [2.1 实现方式](#2.1 实现方式)
- [2.2 AOF文件载入](#2.2 AOF文件载入)
- [2.3 AOF重写](#2.3 AOF重写)
- [2.4 重写触发](#2.4 重写触发)
- [3. RDB vs AOF](#3. RDB vs AOF)
-
- [3.1 RDB](#3.1 RDB)
- [3.2 AOF](#3.2 AOF)
- [3.3 如何选择?](#3.3 如何选择?)
- [4. Redis 4.0 混合持久化](#4. Redis 4.0 混合持久化)
Redis的持久化机制有两种持久化机制,分别是 RDB 和 AOF
1. RDB
Redis Database Backup file(RDB),也被称为Redis数据快照,RDB将内存中的数据都记录在磁盘上,当Redis 出现意外,可以通过重启 Redis 加载该文件来恢复数据。
RDB 默认开启,既可以手动执行,也可以根据服务器配置选项定期 自动执行。
服务器在恢复数据库数据(载入RDB文件)时,会一直处于阻塞状态,直到载入完成为止。
1.1 实现方式
手动执行
- SAVE命令:阻塞 Redis 进程,直到 RDB 文件创建完成为止,这个阶段服务器无法处理任何命令请求
java
save <时间间隔> <执行指定次数更新操作>
- BGSAVE命令:创建一个子进程来创建RDB文件,这个阶段服务器可以处理请求,但是会拒绝SAVE、BGSAVE、BGREWRITEAOF命令
SAVE是阻塞式的,因此其可用性欠佳,如果在数据量较少的情况下,基本上体会不到两个命令的差别,建议使用BGSAVE
自动执行
BGSAVE是非阻塞的,所以Redis允许通过配置 redis.conf 文件中的 save 配置,让服务器每隔一段时间自动执行一次 BGSAVE 命令,可以设置多个保存条件,比如以下3个默认条件
java
# 将数据库保存到磁盘上:
# save <秒数> <更改数>
# 如果满足给定的秒数和对数据库的写操作次数,则保存数据库。
# 在下面的示例中,行为将是:
# 当至少有1个键发生更改时,每900秒(15分钟)保存一次
# 当至少有10个键发生更改时,每300秒(5分钟)保存一次
# 当至少有10000个键发生更改时,每60秒保存一次
# 注意:您可以通过注释掉所有的 "save" 行来完全禁用保存功能。
# 也可以通过添加一个只有一个空字符串参数的保存指令来删除所有先前配置的保存点,如下面的示例:
# save ""
save 900 1 #在900秒(15分钟)之内,对数据库进行了至少1次修改,则执行一次BGSAVE
save 300 10 #在300秒(5分钟)之内,对数据库进行了至少10次修改,则执行一次BGSAVE
save 60 10000 #在60秒之内,对数据库进行了至少10000次修改,则执行一次BGSAVE
1.2 实现原理
当Redis服务启动时,用户可以通过指定配置文件或者传入启动参数的方式设置save选项
如果没有主动设置,服务器就会使用redis.conf文件中默认的条件(上述3个默认条件)。
接着,服务器会根据save的选项所设置的保存条件,设置服务器状态 redisServer 结构的 saveparams 属性,除此之外,还有一个 dirty 计数器 及 lastsave 属性:
java
struct redisServer{
//....
//记录了保存条件的数组
struct saveparam *saveparams;
//修改计数器
long long dirty;
//上一次执行保存的时间
time_t lastsave;
//.....
};
- saveparams属性:是一个数组,每个 saveparams 结构都保存了一个save选项设置的保存条件:
java
struct saveparam{
//秒数
time_t seconds;
//修改数
int changes;
};
-
dirty 属性:记录上一次成功成功执行 SAVE 命令或 BGSAVE 命令之后,服务器对数据库(全部数据库)进行了多少次修改(包括写入、删除、更新等操作)
-
lastsave 属性:是一个UNIX时间戳,记录服务器上一次成功执行 SAVE 或 BGSAVE 命令的时间。
Redis的服务器有一个周期性操作函数serverCron,它每隔100ms就会执行一次,该函数用于对正在运行的服务器进行维护,其中的一项工作就是检查save选项所设置的保存条件是否已满足,如果满足就执行BGSAVE命令。
2. AOF
Append Only File,采用日志的形式来记录每个写操作,并追加到文件中。
默认不开启,可在redis.conf文件中配置
Redis 重启会根据日志文件的内容将写命令从前到后执行一遍来恢复数据,解决生成 RDB 文件后数据不能实时一致的问题
2.1 实现方式
AOF持久化功能的实现可以分为命令追加(append)、文件写入、文件同步(sync)三个步骤
-
命令追加:服务器在执行完一个写命令后,会将被写命令追加到服务器状态的aof_buf缓冲区的末尾
-
文件写入:将aof缓冲区的数据写入到AOF文件,此时数据并没有写入到硬盘,而是拷贝到了内核缓冲区page cache(操作系统),等待内核将数据写入硬盘;具体内核缓冲区的数据什么时候写入到硬盘,由内核决定。
-
文件同步:将内核缓冲区中的数据写入到硬盘中的AOF文件中。
如果由内核决定将内核数据写入硬盘的话,如果服务器宕机,那么就会丢失数据。为了解决这个问题,系统提供了 fync 和 fdatasync 两个同步函数,它们可以强制让操作系统立即将缓冲区中的数据写入到硬盘,以及三种策略:
- always:同步写回,每个写命令执行完立刻同步地将日志写回磁盘。(性能最差,最多丢失一个写指令的数据)
- everysec(默认):每秒执行一次。(是性能和数据安全性的折中方案,最多也就丢一秒的数据)
- no:根据操作系统和资源的情况,一定时间执行一次,时间不确定。(性能最好,可能会丢失上次同步AOF文件之后的所有写命令数据)
2.2 AOF文件载入
因为AOF文件里包含了所有写命令,所以服务器只要读入并重新执行一遍AOF文件里面的命令,就可以还原服务器关闭之前的数据,详细步骤如下:
- 创建一个不带网络连接的伪客户端(fake client):因为Redis的命令只能在客户端上下文中执行,而载入AOF文件时所使用的命令直接来源于AOF文件而不是网络连接,所以服务器使用了一个没有网络连接的伪客户端来执行AOF文件保存的写命令,伪客户端执行命令的效果和带网络连接的客户端执行命令的效果完全一样。
- 从AOF文件中分析并读取出一条写命令。
- 使用伪客户端执行被读出的写命令。
- 一直执行步骤2和步骤3,直到AOF文件中的所有写命令都被处理完毕为止。
当完成以上步骤之后,AOF文件所保存的数据库状态就会被完整地还原出来,流程如下图所示:
2.3 AOF重写
AOF 持久化是通过保存被执行的写命令来记录数据库状态的,所以随着服务器运行时间的流逝,AOF文件中的内容会越来越多,文件的体积也会越来越大
Redis重写功能的大致流程如下:
- Redis 会启动一个 AOF 重写子进程,负责执行 AOF 重写操作。同时,Redis 会继续处理新的写入命令,并将这个写命令发送给AOF缓冲区和AOF重写缓冲区。
- 子进程会按照一定的规则,读取当前数据库的键值对,并将其转化为合适的命令格式,保存到AOF重写缓冲区中。
- 子进程在遍历完整个数据库之后,就完成了重写工作。
- 接着,服务器父进程就会执行以下操作:
- 将AOF重写缓冲区中的所有内容写入到新的AOF文件中,使得新的AOF文件于当前数据库中的数据一致。
- 对新的AOF文件进行改名,原子地(atomic)覆盖现有的AOF文件,完成新旧两个AOF文件的替换。
- 最后,Redis 会关闭并销毁旧的 AOF 文件。
AOF缓冲区:无论重不重写都有这个缓冲区,AOF日志写入是AOF缓冲区->AOF文件
AOF重写缓冲区:AOF重写子进程启动后开始使用。
2.4 重写触发
手动触发:手动执行 BGREWRITEAOF 命令,开始重写 aof 文件
自动触发:可以在 redis.conf 文件中修改对应的配置,让服务器自动执行 BGREWRITEAOF 命令。
3. RDB vs AOF
RDB 和 AOF 在数据可靠性、性能、存储空间、使用场景等方面都有不同的优缺点,具体可以根据实际业务需求和硬件条件选择合适的持久化机制,或者同时使用两种持久化机制来实现更高的数据可靠性
3.1 RDB
优点:
- 只有⼀个紧凑的⼆进制文件 dump.rdb ,非常适合备份、全量复制的场景
- 容灾性好,可以把 RDB 文件拷贝道远程机器或者⽂件系统张,用于容灾恢复。
- 恢复速度快,RDB 恢复数据的速度远远快于 AOF 的⽅式
缺点:
- 实时性低,RDB 是间隔⼀段时间进行持久化,没法做到实时持久化、秒级持久化。如果在这⼀间隔事件发生故障,数据会丢失。
- 存在兼容问题,Redis 演进过程存在多个格式的 RDB 版本,存在老版本 Redis 无法兼容新版本 RDB 的问题。
3.2 AOF
优点:
- 实时性好,aof 持久化可以配置 appendfsync 属性,有 always ,每进行⼀次命令操作就记录到 aof 文件中⼀次。
- 通过 append 模式写⽂件,即使中途服务器宕机,可以通过 redis-check-aof 工具解决数据⼀致性问题。
缺点:
- AOF 文件比 RDB 文件大,且恢复速度慢
- 数据集大的时候,比 RDB 启动效率低
3.3 如何选择?
- 一般来说, 如果想达到足以媲美数据库的 数据安全性,应该同时使用两种持久化功能。在这种情况下,当 Redis 重启的时候会优先载入 AOF 文件来恢复原始的数据,因为在通常情况下 AOF文件保存的数据集要比 RDB 文件保存的数据集要完整。
- 如果 可以接受数分钟以内的数据丢失,那么可以 只使用 RDB 持久化。
- 有很多用户都只使用 AOF 持久化,但并不推荐这种方式,因为定时生成 RDB 快照 (snapshot)非常便于进行数据备份, 并且 RDB 恢复数据集的速度也要比 AOF 恢复的速度要快,除此之外,使用 RDB 还可以避免 AOF 程序的 bug。
- 如果只需要数据在服务器运行的时候存在,也可以不使用任何持久化方式
4. Redis 4.0 混合持久化
重启 Redis 时,我们很少使用 RDB 来恢复内存状态,因为会丢失大量数据。
我们通常使用 AOF 日志重放,但是重放 AOF 日志性能相对 RDB 来说要慢很多,这样在 Redis 实很大的情况下,启动需要花费很长的时间。
Redis 4.0 为了解决这个问题,带来了一个新的持久化选项--混合持久化。
将 rdb 文件的内容和增量的 AOF 日志文件存在一起。
这里的 AOF 日志不再是全量的日志,而是 自持久化开始到持久化结束 的这段时间发生的增量 AOF 日志,通常这部分 AOF 日志很小,如下图所示
于是在 Redis 重启的时候,可以先加载 rdb 的内容,然后再重放增量 AOF 日志就可以完全替代之前的 AOF 全量文件重放,重启效率因此大幅得到提升。
优点:混合持久化结合了 RDB 和 AOF 持久化的优点,开头为RDB格式,可以使Redis启动的更快,同时结合AOF的优点,又降低了大量数据丢失的风险。
缺点 :在 AOF 文件中添加了 RDB 格式的内容,使得 AOF 文件的可读性变得很差 ;如果开始混合持久化,那么混合持久化的 AOF 是不能在旧版本中用的,不能向下兼容。
参考《Redis设计与实现》& 二哥的面渣逆袭,加油!