Redis设计与实现AOF持久化
上一篇:Redis设计与实现-RDB持久化
RDB持久化虽然解决了内存丢失的问题,但是它依然有不少缺点,比如:生成RDB文件耗时,耗资源,生成RDB文件期间数据丢失等问题。
为此Redis提供了另外一种持久化技术AOF
概要
AOF持久化不同于RDB持久化,前者存放的是命令对应的redis协议字符串,而后者存放是压缩后的二进制,格式如下:
比如执行了如下命令:SELECT 0*2
$6
SELECT
$1
0
各部分解释:
*2:命令有2部分
$6:SELECT 为6个字符
$1:0 为1个字符
Redis默认是关闭aof持久化的,可以在redis.conf中配置如下参数
# 开启aof持久化appendonly yes
# 指定文件名
appendfilename "your file name"
# no:重写时会阻塞appendfsync;
# yes:不会阻塞appendfsync,最多可能出现30秒的数据延迟,甚至丢失30秒数据
# 如果将主节点的no-appendfsync-on-rewrite 设置为yes,添加从节点。
no-appendfsync-on-rewrite no
# aof重写触发条件,生产环境一定要修改这两个值
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64M
实现原理
Redis每执行一个命令就会将其写入aof_buf缓冲区中,然后由配置的文件写入-同步策略(三种)决定什么时候真正同步到磁盘上
伪代码:def eventLoop(){
while true:
#处理文本事件,接收命令请求以及发送命令回复
#将命令追加到aof缓冲区
processFileEvents();
#处理时间事件,比如bgsave触发
processTimeEvents();
#判断是否要讲aof缓冲区写入和保存到AOF文件中
flushAppendOnlyFile();
}
在进入三种策略讲解之前得先了解两个知识点:LINUX文件写入原理以及Redis Server执行方式
LINUX文件写入原理
操作系统为了提高写入效率,当用户调用write函数,将一些数据写入到文件的时候,会先将数据写入到一个缓冲区当中,如果缓冲区满了,或者超过指定的时限才真正的将缓冲区的数据写入到磁盘上的文件中。
这种方式会有一定的风险,当数据还未写入磁盘时,机器断电等情况出现会导致数据丢失,不过操作系统提供了fsync和fdatasync同步函数,可以强制让操作系统立即将缓冲区中的数据写入到磁盘上面
Redis Server执行方式
伪代码:def eventLoop(){
while true:
#处理文本事件,接收命令请求以及发送命令回复
#将命令追加到aof缓冲区
processFileEvents();
#处理时间事件,比如bgsave触发
processTimeEvents();
#判断是否要讲aof缓冲区写入和保存到AOF文件中
flushAppendOnlyFile();
}
Redis就是不断的循环执行这段代码,完成所有的命令执行、定时任务的执行
三种文件写入-同步方式
Redis有三种方式:always/everysec/no,其中everysec是Redis默认选项。
在网上看到有部分说法是每个命令执行完成就会立即写进OS缓冲区,但事实上是一个或者一批命令执行完才会写入到OS缓冲区->同步到磁盘
always
每次循环都会写入与同步数据到AOF文件当中
everysec
每次循环都会写入数据到操作系统缓冲区,超过1秒就会将缓冲区的数据写入到文件当中
no
每次循环都会写入数据到OS缓冲区,何时将OS缓冲区中的数据写入到文件由OS决定
比较
always是最安全的,但是效率最慢,因为每次循环都要写文件,大家都知道IO是相当慢的。
everysec是一种折中方案,数据库也只会丢失一秒钟的命令数据。
no是最快的,因为不会调用磁盘同步函数,没有磁盘的IO,所以效率是很快。不过因为断点可能丢失数据。
看到这里从RDB数据丢失的时间范围由RDB执行时间缩短至1秒钟,Redis不能保证完全不丢失数据。
启动Redis-Server载入
当服务器启动时候先判断是否开启了AOF持久化,如果开启了,则会读取AOF文件,一条命令一条命令分析并执行,直到所有命令执行完成,则整个数据库恢复完成,因此这个过程肯定比RDB慢。
AOF重写
因为这个文件一直是append的方式写入的,当服务器运行时间很长很长以后,文件会很大,会消耗磁盘资源,重启服务也会很慢。因此Redis提供了重写机制。
重写会将原来很大的AOF文件缩小很多,为什么喃???
因为AOF文件当中存在很多过时或者无效的命令,比如对同一个键执行了很多次操作,其实重写的时候只需要保存最后的结果即可。
redis> set foo 1redis> set foo 2
redis> set foo 3
redis> set foo 4
AOF重写的时候只需要执行最后一条set foo 4即可保证数据正确性和一致性。
看到这里应该会问:为什么Redis会知道应该只执行最后一条命令喃???
因为Redis fork一个子进程执行AOF文件重写(产生一个新文件)时,写入文件的命令是从数据库(内存)中的数据恢复出来的,而不是解析原来的AOF文件。。。这种方式会比解析原来的AOF文件快很多,而且能够将AOF文件最小化,命令最少化。
另外:
AOF文件重写时,如果是列表,集合类等键,命令不可能无限长,需要根据一个常量(64个Item)将命令拆分为多个,这样拆分的目的是为了避免客户端输入缓冲区溢出,导致数据不全或者执行命令报错
既然从数据库中恢复数据,那么在执行重写的时候新执行的命令如何保证写入到新的AOF文件中喃???
AOF重写的时候,Redis服务器进程还在继续接收命令,为了避免数据不一致,Redis还有一个AOF重写缓冲区。当完成从数据库中重写AOF文件之后,Redis会调用一个信号处理函数,完成如下工作:
将AOF重写缓冲区的内容写到AOF文件中
将AOF文件改名为正确的文件名
这个信号函数执行的时候,REDSI服务器进程是无法接受命令的,因此会阻塞客户端请
以上是 Redis设计与实现AOF持久化 的全部内容, 来源链接: utcz.com/z/512390.html