初探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命令。
来源:https://www.cnblogs.com/smallhehe/p/12253835.html