在运行情况下,Redis 以数据结构的形式将数据维持在内存中,为了让这些数据在 Redis 重启之后仍然可用,需要将数据写入持久存储
持久化是指将数据写入持久存储,例如固态磁盘(SSD)
Redis 提供了一系列持久化选项。这些包括:
RDB(Redis Database)
:将数据库的快照(snapshot)以二进制的方式保存到磁盘中AOF(Append Only File)
:以协议文本的方式,将所有对数据库进行过写入的命令(及其参数)记录到 AOF 文件,以此达到记录数据库状态的目的RDB + AOF
:RDB和AOF混合方式(4.0版本)RDB详解见Redis持久化——RDB机制详解
本文首先介绍 AOF 功能的运作机制,了解命令是如何被保存到 AOF 文件里的,观察不同的 AOF 保存模式对数据的安全性、以及 Redis 性能的影响。
之后会介绍从 AOF 文件中恢复数据库状态的方法,以及该方法背后的实现机制。
最后还会介绍对 AOF 进行重写以调整文件体积的方法,并研究这种方法是如何在不改变数据库状态的前提下进行的
AOF文件使用网络通讯协议的格式来保存这些命令
若执行以下命令:
redis> RPUSH list 1 2 3 4
(integer) 4redis> LRANGE list 0 -1
1) "1"
2) "2"
3) "3"
4) "4"redis> KEYS *
1) "list"redis> RPOP list
"4"
那么其中两条对数据库有修改的写入命令就会被同步到 AOF 文件中:
RPUSH list 1 2 3 4RPOP list
上面列举的两个命令在 AOF 文件实际保存如下:
*2
$6
SELECT
$1
0
*6
$5
RPUSH
$4
list
$1
1
$1
2
$1
3
$1
4
*2
$4
RPOP
$4
list
除了 SELECT 命令是 AOF 程序自己加上去的之外, 其他命令都是之前我们在终端里执行的命令
*
表示后面语句的词个数,$
表示后面词的字节数
同步命令到 AOF 文件的整个过程可以分为三个阶段:
fsync
函数或者fdatasync
函数会被调用,将写入的内容真正地保存到磁盘中当一个 Redis 客户端需要执行命令时,它通过网络连接,将协议文本发送给 Redis 服务器
比如说,要执行命令SET KEY VALUE
,客户端将向服务器发送文本*3\r\n$3\r\nSET\r\n$3\r\nKEY\r\n$5\r\nVALUE\r\n
服务器在接到客户端的请求之后,它会根据协议文本的内容,选择适当的命令函数,并将各个参数从字符串文本转换为 Redis 字符串对象(StringObject)
每当命令函数成功执行之后, 命令参数都会被传播到 AOF 程序, 以及 REPLICATION 程序
当命令被传播到 AOF 程序之后,程序会根据命令以及命令的参数,将命令从字符串对象转换回原来的协议文本
缓存追加过程可以分为以下三步:
aof_buf
末尾协议文本生成之后,它会被追加到redis.h/redisServer
结构的aof_buf
末尾
redisServer
结构维持着 Redis 服务器的状态,aof_buf
域则保存着所有等待写入到 AOF 文件的协议文本
struct redisServer {// 其他域...sds aof_buf;// 其他域...
};
每当服务器常规任务函数被执行、或者事件处理器被执行时,aof.c/flushAppendOnlyFile
函数都会被调用,这个函数执行以下两个工作:
WRITE
:根据条件,将aof_buf
中的缓存写入到 AOF 文件SAVE
:根据条件,调用fsync
或fdatasync
函数,将 AOF 文件保存到磁盘中两个步骤都需要根据一定的条件来执行 而这些条件由 AOF 所使用的保存模式来决定。以下会介绍 AOF 所使用的三种保存模式,以及在这些模式下,步骤WRITE和SAVE的调用条件
Redis 目前支持三种 AOF 保存模式
Always
,同步写回:每个写命令执行完,立马同步地将日志写回磁盘Everysec
,每秒写回:每个写命令执行完,只是先把日志写到AOF文件的内存缓冲区,每隔一秒把缓冲区中的内容写入磁盘No
,操作系统控制的写回:每个写命令执行完,只是先把日志写到AOF文件的内存缓冲区,由操作系统决定何时将缓冲区内容写回磁盘根据键的类型,使用适当的写入命令来重现键的当前值,这就是 AOF 重写的实现原理
所谓的“重写”其实是一个有歧义的词语,实际上 AOF 重写并不需要对原有的 AOF 文件进行任何写入和读取,它针对的是数据库中键的当前值
AOF 重写是一个有歧义的名字,实际的重写工作是针对数据库的当前值来进行的,程序既不读写、也不使用原有的 AOF 文件
AOF 后台重写,是为了避免主进程被阻塞,无法处理请求,所以采用主进程fork
出子进程,用于 AOF 重写。为了避免在 AOF 重写期间新命令对现有数据的修改导致的不一致问题,Redis 增加了一个AOF 重写缓存,这个缓存在fork
出子进程之后开始启用,Redis 主进程在接到新的写命令之后,除了会将这个写命令的协议内容追加到现有的 AOF 文件之外,还会追加到这个缓存中
换言之, 当子进程在执行 AOF 重写时, 主进程需要执行以下三个工作:
当子进程完成 AOF 重写之后,它会向主进程发送一个完成信号,主进程在接到完成信号之后,会调用一个信号处理函数,并完成以下工作:
以上就是 AOF 后台重写, 也即是BGREWRITEAOF命令的工作原理
fsync
策略,fsync
是使用后台线程执行的,写入性能很好redis-check-aof
工具也能够轻松修复它对于相同的数据集,AOF 文件通常比等效的 RDB 文件大
根据确切的 fsync 策略,AOF 可能比 RDB 慢
Redis < 7.0
如果在重写期间有对数据库的写入,AOF 会使用大量内存
重写期间到达的所有写入命令都会写入磁盘两次
Redis 可能会在重写结束时冻结写入并将这些写入命令同步到新的 AOF 文件
Redis 4.0 中提出了一个混合使用 AOF 日志和内存快照的方法。简单来说,内存快照以一定的频率执行,在两次快照之间,使用 AOF 日志记录这期间的所有命令操作。
这样一来,快照不用很频繁地执行,这就避免了频繁 fork 对主线程的影响。而且,AOF 日志也只用记录两次快照间的操作,也就是说,不需要记录所有操作了,因此,就不会出现文件过大的情况了,也可以避免重写开销。
如下图所示,T1 和 T2 时刻的修改,用 AOF 日志记录,等到第二次做全量快照时,就可以清空 AOF 日志,因为此时的修改都已经记录到快照中了,恢复时就不再用日志了。
这个方法既能享受到 RDB 文件快速恢复的好处,又能享受到 AOF 只记录操作命令的简单优势, 实际环境中用的很多。
参考资料:
上一篇:postman接口关联
下一篇:量价结合指标副图