《Redis设计与实现》阅读笔记6-AOF持久化

10 AOF(Append Only File)

10.1 AOF是什么

以日志的形式来记录每个写操作,将Redis执行过的所有写指令记录下来(读操作不记录),
只许追加文件但不可以改写文件,redis启动之初会读取该文件重新构建数据,换言之,redis重启的话就根据日志文件的内容将写指令从前到后执行一次以完成数据的恢复工作

10.1.1 Aof保存的是appendonly.aof文件


文件名由redis.conf中配置,默认为appendonly.aof,生成的位置,若未进行配置,默认产生在redis当前工作路径下

10.1.2 开启AOF功能


在redis.conf中修改默认的appendonly no,改为yes

10.1.3 AOF恢复数据库

将有数据的aof文件复制一份保存到对应目录,在需要恢复数据库时,将aof放在启动路径下,启动redis。
注:若AOF文件和RDB文件同时存在,默认使用AOF文件进行启动。若数据库是由于flushall等语句丢失,需要人为修改AOF文件,删掉这句话

10.1.4 AOF文件的修复

若由于突然断电等异常导致AOF文件写入乱码,又或者人为修改出错,我们除了可以人为比对进行修改外,也可以使用redis自带的修复工具

redis-check-aof --fix *.aof

10.2 持久化的实现

AOF持久化功能分为命令追加,文件写入,文件同步三个步骤

10.2.1 命令追加

在redisServer结构中有个aof_buf的属性,是一个缓冲区,被用于储存协议格式的写命令。

struct redisServer{
    //...

    //AOF缓冲区
    sds aof_buf;

    //...
};

10.2.2 AOF文件写入与同步

为了提高文件的写入效率,在现代操作系统中,当用户调用write函数时,将某些数据写入到文件时,操作系统会将这些数据暂时保存在一个内存缓冲区中,等到缓冲区被填满或超过某个限定时,才会真正将数据数据写入磁盘。

这样虽然提高了效率,但带来了安全问题(宕机会导致内存缓冲区的数据丢失)。

系统提供了fsync和fdatasync两个同步函数,可以强制让操作系统立即将缓冲区的数据写到磁盘里面

appendfsync后面的参数有三种always,everysec(默认),no

  • 每修改同步:appendfsync always 每次事件循环中都立即将缓冲区的数据所有内容写入AOF文件,并同步AOF文件,性能是三种方式中最差的,但数据完整性比较好,宕机只会丢失最后一次事件循环里面的数据 。

  • 每秒同步:appendfsync everysec 每次事件循环中都立即将缓冲区的数据所有内容写入AOF文件,如果上次同步AOF文件的时间距离现在超过一秒钟,那么就再次对AOF文件进行同步。效率上来讲,这种模式足够快,宕机也只会丢失最近一秒内的数据

  • 不同步:appendfsync no 将aof_buf缓冲区所有内容写入到AOF文件,但并不对AOF文件进行同步,何时同步由操作系统来决定。写入速度最快,因为这个模式将数据积累一段时间后再同步,所以单次同步时间最长,将时间平摊开和everysec 模式差不多,但宕机会丢失缓冲区的所有数据。

10.3 重写

  • AOF采用文件追加方式,文件会越来越大为避免出现此种情况,新增了重写机制,当AOF文件的大小超过所设定的阈值时,Redis就会启动AOF文件的内容压缩,只保留可以恢复数据的最小指令集.可以使用命令bgrewriteaof

  • 原理:AOF文件持续增长而过大时,会fork出一条新进程来将文件重写(先写临时文件最后再rename),遍历新进程的内存中数据。重写aof文件的操作,并没有读取旧的aof文件,而是将整个内存中的数据库内容用命令的方式重写了一个新的aof文件,这点和快照有点类似

  • 触发条件:Redis会记录上次重写时的AOF大小,默认配置是当AOF文件大小是上次rewrite后大小的一倍且文件大于64M时触发

10.3.1 AOF文件重写的实现

  • 首先会创建新的AOF文件,接着遍历数据库,遍历的过程中会直接忽略空的数据库,不空的情况下,遍历数据库中的所有键。

  • 将键在新的AOF文件中进行重写,如果键带有过期时间,会把过期时间也一起重写。

  • 接着关闭文件,开始重写值,遍历所有的键,对每个键进行相应的查询操作(get获取字符串键对应的值,lrange获取列表键对应的值…)

  • 将得到的值,对应键在新的AOF文件中进行重写操作

注意:实际中,为避免客服端的输入缓冲区溢出,重写程序在处理列表,哈希表,集合,有序集合这四种可能带有多个元素的键时,会先检查键所包含的元素数量,如果这个数量超过了redis.h/REDIS_AOF_REWRITE_ITEMS_PER_CMD常量值,那么重写会分多条操作来进行重写,这个常量的值为64、也就是例如一个集合的元素有192个,那么重写的sadd语句会有三句,才能重写完这一个集合键。

10.3.2 AOF后台重写

因为Redis是单线程的,而重写AOF文件,伴随着大量的写入操作,所以重写会导致线程被长时间阻塞,导致服务器将长时间无法处理客服端发来的命令请求

所以Redis将AOF的重写程序放到了子进程里执行,这样做的目的有两个

  • 子进程进行AOF重写期间,服务期进程(父进程)可以继续处理命令请求

  • 子进程带有父进程的资源与数据副本,子进程不是线程,可以在不使用锁的情况下,保证数据的安全性

但同时这样的机制也带来了问题,在子进程进行AOF重写的时候,如果父进程对数据库进行了新增或修改,就导致服务器当前的数据库状态与重写后AOF文件所保存的数据库状态不一致

为了解决这个问题,Redis又设置一个AOF重写缓冲区,这个缓冲区在服务器创建子进程以后才使用,当Redis执行完一个命令,他会将写命令同时发给AOF缓冲区与AOF重写缓冲区

这样就保证:

  • AOF缓冲区的内容会定期被写入和同步到AOF文件,对现有AOF文件的处理工作照常进行
  • 从创建子进程开始,服务器执行的所有写命令都会被记录到AOF重写缓冲区里面

当重写完成以后,子进程会向父进程发送一个信号,父进程接收到这个信号之后,会调用信号处理函数

  • 将AOF重写缓冲区的所有内容写入到新AOF文件中,这是新AOF文件所保存的数据库状态和服务器当前的数据状态一致
  • 对新的AOF文件进行改名,原子性的覆盖现有的AOF文件,完成新旧AOF文件的替换
  • 整个重写过程只有信号处理函数会对服务器进程造成阻塞

10.4 与RDB持久化方式的对比

10.4.1 优劣

  • 优点

    • AOF文件数据损失一般最多丢失最近一秒内的数据,比RDB的最大丢失小很多
  • 缺点

    • 相同数据集的数据而言aof文件要远大于rdb文件,恢复速度慢于rdb
    • aof运行效率要慢于rdb,每秒同步策略效率较好,不同步效率和rdb相同

10.4.2 总结

  • RDB持久化方式能够在指定的时间间隔能对你的数据进行快照存储
  • AOF持久化方式记录每次对服务器写的操作,当服务器重启的时候会重新执行这些命令来恢复原始的数据,AOF命令以redis协议追加保存每次写的操作到文件末尾.Redis还能对AOF文件进行后台重写,使得AOF文件的体积不至于过大
  • 只做缓存:如果你只希望你的数据在服务器运行的时候存在,你也可以不使用任何持久化方式.
  • 同时开启两种持久化方式
    • 在这种情况下,当redis重启的时候会优先载入AOF文件来恢复原始的数据,因为在通常情况下AOF文件保存的数据集要比RDB文件保存的数据集要完整.
    • RDB的数据不实时,同时使用两者时服务器重启也只会找AOF文件。那要不要只使用AOF呢?
      建议不要,因为RDB更适合用于备份数据库(AOF在不断变化不好备份),快速重启,而且不会有AOF可能潜在的bug,留着作为一个万一的手段。
  • 因为RDB文件只用作后备用途,建议只在Slave上持久化RDB文件,而且只要15分钟备份一次就够了,只保留save 900 1这条规则。如果Enalbe AOF,好处是在最恶劣情况下也只会丢失不超过两秒数据,启动脚本较简单只load自己的AOF文件就可以了。代价一是带来了持续的IO,二是AOF rewrite的最后将rewrite过程中产生的新数据写到新文件造成的阻塞几乎是不可避免的。只要硬盘许可,应该尽量减少AOF rewrite的频率,AOF重写的基础大小默认值64M太小了,可以设到5G以上。默认超过原大小100%大小时重写可以改到适当的数值。如果不Enable AOF ,仅靠Master-Slave Replication 实现高可用性也可以。能省掉一大笔IO也减少了rewrite时带来的系统波动。代价是如果Master/Slave同时倒掉,会丢失十几分钟的数据,启动脚本也要比较两个Master/Slave中的RDB文件,载入较新的那个。

猜你喜欢

转载自blog.csdn.net/maniacxx/article/details/82704589