【学习Redis系列】AOF持久化

流过昼夜 提交于 2020-02-02 22:54:49

初探AOF文件

Redis还提供了AOF持久化功能,与RDB持久化不同之处在于,RDB文件保存的数据库的键值对。AOF文件保存的是服务器执行的写入命令。

AOF持久化开关可以在配置文件中appendonly设置,如下图所示。 加载配置会将appendonly赋值到redisServer中的aof_state变量。

 


 举个AOF文件栗子:

 向服务器的默认数据库中存入了三个键,使用BGREWRITEAOF命令手动生成了一份AOF文件,查看这个文件内容,可以看到保存的内容如下

*2$6SELECT$10                    // 此条记录由服务器自动添加,即选择当前数据库。

*5$4SADD$6fruits$5peach$6banana$5apple        

*5$5RPUSH$7numbers$3128$3256$3512

*3$3SET$7message$5hello


AOF持久化的实现

1.命令追加:服务器执行完一个写命令时,会将执行的命令追加到aof_buf缓冲区的末尾.aof_buf 是redisServer中的成员,如下图所示

2.文件写入:Redis的服务器进程就是一个事件循环(loop),在这个循环中处理文件事件(比如客户端的读写),时间事件(执行serverCron函数)

在serverCron函数中会调用aof.c / flushAppendOnlyFile函数,将aof_buf缓冲区的内容写入和保存到AOF文件中。

flushAppendOnlyFIle函数的行为会根据redisServer中aof_fsync的不同值表现不同的效果。aof_fsync的值由配置文件中

的appendfsync来设置。

  • always       将aof_buf缓冲区的内容写入aof文件并进行文件同步(即立即写入磁盘) 。效率上看这种方法是最慢的,但是从数据安全性上来看是最高的。最多丢失一个事件循环的数据。即一次loop循环中的数据。
  • everysec    将aof_buf缓冲区的内容写入aof文件,如果上次同步aof文件的时间距离现在超过1秒钟,则进行文件同步。这种方法足够快,最多丢失1秒的数据。
  • no         将aof_buf缓冲区的内容写入aof文件,具体同步根据操作系统来决定。

TIps:操作系统通常将写入的数据暂时保存在一个内存缓冲区中,当缓冲区被填满,或者超过了指定时限之后,才真正将缓冲区数据写入磁盘里面。这种做法提高了效率,但也引发了安全问题。如果机器断电,则缓冲区未能写入磁盘的数据将丢失。

3.文件同步:将内存缓冲区的数据立即写入磁盘中,系统提供了fsync和fdatasync两个同步函数,强制让操作系统立即将缓冲区数据写入硬盘中,保障了数据的安全性。


 AOF重写

现在知道了AOF文件记录的是服务器完成的写入命令(订阅消息这种可能改变了状态的消息也会写入,通过客户端表示flag强制写入,改日再细细总结),如果对一个键进行了比如100次的写操作 各种增删改查,那还有必要把这100次全都按顺序记录么。是不是直接只记录结果即可。比如

此时这四条写入命令其实可以用一条 RPUSH list “B” “C” “D” 代替。按照这种套路来重新写一个AOF文件,用这个新的AOF文件替换旧的AOF文件,即称为AOF重写。

这个重写行为与原AOF文件毫无关系。其实就是遍历读取数据库中的所有键,保存键的值到新的AOF文件中。这个还是挺好理解的。

这样重写的新AOF文件,与通过写命令追加aof_buf缓冲区,再写入的旧AOF文件相比,要瘦身很多。

AOF文件重写,会有大量的写入操作,所以执行这个函数的线程将会被阻塞一段时间,所以Redis创建子进程执行这个命令。即AOF后台重写。

采用子进程重写,父进程在重写期间可以进行命令请求,不受影响。

为何采用子进程,而不是线程重写:子进程带有服务器数据副本,因此子进程与父进程不存在竞态关系。保障了数据安全性。

AOF后台重写时几个思考问题:

1.AOF文件后台重写时,如果一个键所包含的元素数量超过redis.h/REDIS_AOF_REWRITE_ITEMS_PER_CMD常量的值,则重写程序将使用多条命令记录键的值,这是为了当AOF文件载入时,防止客户端输入缓冲区溢出而采取的做法(所有的命令执行前会先进入客户端的输入缓冲区中,服务器将输入缓冲区中的指令进行解析。)

2.AOF文件后台重写时,又有新的命令写入,此时会出现数据不一致。为了应对这种情况,Redis设置了一个AOF重写缓冲区,这个缓冲区在服务器创建子进程之后使用。服务器执行完一个写命令后,会追加写入aof_buf缓冲区的同时,也追加写入这个AOF重写缓冲区。

这样保证了,AOF缓冲区的内容会被写入当前这个旧的AOF文件中;当子进程完成写入新的AOF文件后,会让父进程发送一个信号。父进程调用信号处理函数执行AOF重写缓冲区对新的AOF文件的写入。这样保证了两个AOF文件的数据一致性。

然后对新的AOF文件进行改名,并覆盖旧的AOF文件。至此,AOF文件后台重写就算完成了。

后台重写函数 aof.c / int rewriteAppendOnlyFileBackground(void); 与RDB后台重写类似的是,Redis服务器采用aof_child_pid记录是否正在进行重写,aof_child_pid后台重写时,为子进程PID,重写完成后将重新复制为-1.

从这也能看出 与RDB执行的异曲同工之妙,不能在执行BGREWIRTEAOF时,再次执行BGREWIRTEAOF;如果正在执行RDB的后台写入即BGSAVE,则标记aof_rewrite_scheduled为1,等待BGSAVE执行后,再执行BGREWRITEAOF命令。

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!