《Redis设计与实现》读书笔记(十五) ——Redis AOF持久化原理与实现

《Redis设计与实现》读书笔记(十五) ——Redis AOF持久化原理与实现

(原创内容,转载请注明来源,谢谢)

一、概述

AOF(Append OnFile)是redis另一种持久化的方式,是通过保存redis服务器写操作的命令来记录数据库的变化。即aof不保存键值对数据,而是保存每一个写操作的语句。流程如下图所示:

aof文件是以redis的命令请求协议格式保存,可以直接打开。例如下图:

上述是在0号数据库,执行了set、sadd、rpush三个命令的结果。

redis服务器启动之前,会载入aof文件,使服务器恢复到关闭之前的状态。

二、aof持久化的实现

1、写入

当aof持久化打开后,每执行一个写命令,redis会以协议格式将其追加到服务器aof_buf缓冲区的末尾。结构体如下:

struct redisServer{
sds aof_buf;//aof缓冲区
}

2、同步

redis服务器进程事件就是一个事件循环,这个循环中的文件事件负责接收客户端的命令请求,以及向客户端发送命令回复;时间事件则是执行类似serverCron定时函数。

每次服务器处理文件事件,即将结束时都会调用flushAppendOnlyFile函数,判断是否需要将aof_buf的缓冲区内容写入aof文件。

flushAppendOnlyFile函数的行为由配置文件appendfsync决定,如果是always则表示将aof_buf写入并同步到aof文件;everysec表示写入aof文件,如果距离上次同步超过1秒则也同步,否则不同步;no表示只写入aof,不同步。

上述的同步,指的是强制要求操作系统进行同步。操作系统对于写入磁盘有策略,并不是每次执行写入就直接写入磁盘,而是会先写入内存,在一定时间或者系统正常关闭的情况下,才会将内容写入文件,这样可以减少操作系统的i/o,加快速度。而这样存在风险,如果还没写入文件系统掉电会宕机,则数据会丢失。因此redis根据操作系统提供的强制写入接口,允许用户通过配置文件的方式要求写入aof的时刻。

上述配置的值,直接决定服务器的效率与安全性,当always时,安全性最高,但是效率最低;no时效率最高,安全性最低;everysec居中。系统默认配置是everysec。

三、aof文件载入与还原

读入流程如下:

1)创建一个不带网络链接的伪客户端(fake client)。由于redis命令只能在客户端执行,而载入的时候是逐行读取aof文件,不需要网络链接。因此使用伪客户端可以正常执行aof文件的命令。

2)从aof文件中读取一条redis命令。

3)用伪客户端执行该redis命令。

4)重复步骤2、3,直到所有写命令处理完毕。

流程图如下:

四、aof重写

1、重写过程

随着时间推移,写命令会逐渐堆积,文件越来越大,而且还原速度会越来越慢。如对同一个键,区分多次进行操作,则在aof中也是多行记录来记录,并不是记录最后一次状态。这样效率较低,则需要aof重写。

重写的命令是bgrewriteaof。

redis对aof文件的重写,并不是真的打开aof文件并分析里面的内容,而是根据redis服务器现有的状态来实现的。为了重写aof,redis会直接读取数据库现有的状态,并自动生成相应的写语句。例如有个list里面有一个键是lists,值是a b c d,redis可以直接生成语句lpush lists a b c d。

整个重写过程如下:

1)忽略空数据库,只考虑有键值对的数据库,并调用select命令进入相应的数据库。

2)创建新的aof文件。

3)遍历所有的键,其中忽略已经过期的键。针对不同的键的数据类型,使用不同的方式生成redis写命令,并且会将每个键相应的过期时间也写入。

4)写入完毕后,关闭文件,重写完成。

由于aof文件的生成,只包含还原当前数据库所需的命令,因此不会浪费硬盘空间。

例如:

上述数据库,aof重新的时候,aof文件会生成以下命令:

select 0
rpush alphabet a b c
expireat alphabet 1385877600000
hmset book name ‘Redis in Action’ author ‘Josiah L. Carlson’publisher Manning
expireat book 1388556000000
set message ‘hello world’

如果列表、集合、有序集合、哈希的元素超过redis.h/REDIS_AOF_REWRITE_ITEMS_PER_CMD常量定义的值,则生成的aof文件不会是一个键一个语句,会用多条语句来处理某个键,避免单条语句执行超出缓冲区。

2、后台重写

redis执行aof重写的过程,会创建一个子进程进行处理,而服务器继续处理客户端的请求。

但是,这样会有一个问题,重新过程如果客户端对键进行操作,则有可能导致新的aof文件与服务器键值对状态不一致。

为了避免此问题,redis设置一个aof重写缓冲区,当在执行aof重写期间,redis完成一个客户端的写请求后,会同时将这个命令发送给aof缓冲区与aof重写缓冲区。如下图所示:

aof重写完成后,会像父进程发送一个信号,父进程接收到信号后,会调用一个信号处理函数,并执行以下工作:

1)将aof重写缓冲区的所有内容重新写入aof文件,使新aof文件与数据库当前状态保持一致。

2)对新的aof文件进行改名,覆盖原aof文件。

在整个aof重写过程中,只有最后的信号处理函数会使服务器主进程阻塞。即将aof重写缓冲区写入aof文件的时候,不处理客户端发送的请求;重命名期间也不处理客户端发送的请求。

五、总结

1、aof文件通过保存修改服务器数据库的写命令请求,来记录服务器的状态。

2、aof文件以redis请求协议格式保存,可以用本文打开。

3、命令请求会先写入aof缓冲区,之后再定期同步到aof文件。

4、appendsync可以设置同步到aof文件的间隔,有立即(always)、每1秒(everysec)、从不(no)三种方式,其安全性逐渐降低,效率逐渐增高。默认采用每1秒同步一次,即everysec。

5、服务器只要载入并重新执行aof文件里面的redis命令,就可以恢复到原来的状态。

6、aof重新会根据当前服务器的数据,生成redis命令,写入新的aof文件,并且会覆盖原aof文件,以减少aof文件的内容,加快恢复速度。

7、在执行bgrewriteaof命令,redis会先创建一个aof重写缓冲区,并创建一个子进程,由子进程进行aof重写的工作,主进程仍处理客户端的请求。在重写过程中,如果客户端发来写命令,除了主进程执行写命令外,还会将该命令写入aof重写缓冲区中。当子进程完成aof重写工作,会给主进程发送一个信号,主进程会调用信号处理函数,在此期间服务器会阻塞,不处理客户端的请求。信号处理函数首先会将aof重写缓冲区的内容写入新的aof文件,接着将新的aof文件重命名为原来的aof文件的名称,并覆盖原来的aof文件。

——written by linhxx 2017.09.05

原文发布于微信公众号 - 决胜机器学习(phpthinker)

原文发表时间:2017-09-05

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Samego开发资源

Dockerfile再恋笔记 | 详解篇

2043
来自专栏Ceph对象存储方案

源码解读bucket 删除中的一些细节

问题描述 社区群里有人说删除bucket以后还有部分数据残留,用的ceph 10.2.x版本做的验证 测试用例 from boto.s3.connection ...

22210
来自专栏散尽浮华

nginx域名访问的白名单配置梳理

在日常运维工作中,会碰到这样的需求:设置网站访问只对某些ip开放,其他ip的客户端都不能访问。可以通过下面四种方法来达到这种效果: 1)针对nginx域名配置所...

65210
来自专栏十月梦想

node表单文件上传(formidable)实现

在node表单进行上传时候,常规的数据传递没有什么问题,当涉及到文件上传(图片,音视频,文本等)我们发现,接收的仅仅是这个上传的文件名,而非资源本身,这样如何能...

621
来自专栏Android相关

IjkPlayer数据读取-read_thread

在stream_open函数中,初始化完视频,音频,字幕的帧队列后,启动了两个线程

703
来自专栏李家的小酒馆

复用$.ajax方式传递参数错误处理

2. 前台拼接方式不和普通post一样,要和使用get请求的时候拼接的方式一致,如下。

521
来自专栏LuckQI

Docker容器中学习系列十一~一个利器DockerFile指令详解

在我们学习容器中我们往往都会用到DockerFile这个文件,在前两篇的文章中,我们也着重的使用了DockerFile这个文件用来构建我们使用的容器,这样可以用...

733
来自专栏Golang语言社区

编写一个go gRPC的服务

前置条件: 获取 gRPC-go 源码 $ go get google.golang.org/grpc 简单例子的源码位置: $ cd $GOPATH/src/...

4147
来自专栏佳爷的后花媛

我的PHP常用代码段

后台进行修改操作时,没有修改图片,再次刷新图片地址为空? 需要在修改函数中加入一个去除空字符串的函数.

642
来自专栏从零学习云计算

openshift/origin学习记录(6)——集群节点管理

采用Cockpit实现集群节点管理。Cockpit是一个开源的系统管理项目。支持Docker、Kubernetes、Openshift。 安装Cockpi...

2130

扫码关注云+社区