AOF(Append Only File)持久化,通过保存服务器所执行的写命令来记录数据库状态
被写入 AOF 文件的所有命令都是以 Redis 命令请求协议保存的,即纯文本格式
三个步骤:
AOF 打开时,服务器在执行完写命令后以协议格式将写命令追加到服务器状态的 aof_buf 缓冲区末尾
Redis 服务器进程是一个事件循环,循环中的文件事件负责接收客户端的命令请求、向客户端发送命令回复;时间事件负责执行类似 sercerCron 的定时运行函数
服务器每次结束一个事件循环之前,会调用 flushAppendOnlyFile 函数,考虑是否需要将 aof_buf 缓冲区中的内容写入和保存到 AOF 文件中
flushAppendOnlyFile 函数行为由配置的 appendfsync 值决定
默认为 everysec
文件的写入和同步
为了提高写入效率,用户调用 write 函数,将数据写入到文件时,操作系统通常会将写入的数据暂时保存在一个内存缓冲区中,等缓冲区填满或者超过指定时限后,才将缓冲区数据写入到磁盘中
但如果缓冲区数据还未写入前计算机发生故障,缓冲区数据将会丢失
系统提供 fsync 和 fdatasync 函数,强制让操作系统将缓冲区数据立即写入
AOF 持久化效率和安全性
读入文件并重新执行一遍写命令就可以还原服务器关闭之前的数据库状态
详细步骤:
随着服务器运行,AOF 文件内容越来越多,体积越来越大,还原的时间越多,于是就有了 AOF 重写功能
AOF 文件重写功能:创建新的 AOF 文件来代替现有的 AOF 文件,数据库状态相同,但不会包含任何浪费空间的冗余命令,于是体积会比较小
实际上 AOF 文件重写并不需要对现有的 AOF 文件进入任何读取、分析或者写入操作,而是通过读取服务器当前的数据库状态来实现的
假设对一个键值对进行了非常多的操作,重写只需要找到最终的状态,并用一条命令去实现这个状态即可
127.0.0.1:6379> rpush list "A" "B"
(integer) 2
127.0.0.1:6379> rpush list "C"
(integer) 3
127.0.0.1:6379> rpush list "D" "E"
(integer) 5
127.0.0.1:6379> lpop list
"A"
127.0.0.1:6379> lpop list
"B"
127.0.0.1:6379> rpush list "F" "G"
(integer) 5
127.0.0.1:6379> lrange list 0 4
1) "C"
2) "D"
3) "E"
4) "F"
5) "G"
以上命令可精简为
127.0.0.1:6379> rpush list "C" "D" "E" "F" "G"
(integer) 5
首先从数据库读取键现在的值,用一条命令去记录键值对,代替之前记录这个键值对的多条命令
伪代码如下:
实际中,为了避免命令时造成客户端输入缓冲区溢出,重写程序在处理列表、哈希表、集合、有序集合这四种可能带有多个元素的键时,会检查元素数量,如果超过了 redis.h/REDIS_AOF_REWRITE_ITEMS_PER_CMD (默认为64)常量的值,那么重写程序将使用多条命令来记录键的值
aof_rewrite 会进行大量的写入,调用这个函数的线程会被堵塞,如果是单线程工作的话将无法处理客户端发来的命令请求
于是 AOF 重写在子程序里运行:
服务器父进程仍在接收客户端命令,可能有命令造成数据库状态修改,使得当前的数据库状态和重写之后的 AOF 文件保存的数据库状态不一致
解决:
Redis 设置了一个 AOF 重写缓冲区,在服务器创建子进程后开始使用,当服务器执行完写命令后,同时将写命令发给 AOF 缓冲区和 AOF 重写缓冲区
在子进程执行 AOF 重写时,服务器进程有以下三个工作:
保证:
子进程完成 AOF 重写后向父进程发送信号,父进程接收后调用函数执行工作:
将 AOF 重写缓冲区内容写入新的 AOF 文件
AOF 文件改名,原子地覆盖现有的 AOF 文件
此期间阻塞父进程
原文:https://www.cnblogs.com/zephxu/p/14923772.html