zoukankan      html  css  js  c++  java
  • Redis核心解读–AOF与REWRITE机制

    Redis AOF 简介

    Redis AOF是类似于log的机制,每次写操作都会写到硬盘上,当系统崩溃时,可以通过AOF来恢复数据。每个带有写操作的命令被Redis服务器端收到运行时,该命令都会被记录到AOF文件上。由于只是一个append到文件操作,所以写到硬盘上的操作往往非常快。

    其实Redis oaf机制包括了两件事,rewrite和AOF。rewrite类似于普通数据库管理系统日志恢复点,当AOF文件随着写命令的运行膨胀时,当文件大小触碰到临界时,rewrite会被运行。
    rewrite会像replication一样,fork出一个子进程,创建一个临时文件,遍历数据库,将每个key、value对输出到临时文件。输出格式就是Redis的命令,但是为了减小文件大小,会将多个key、value对集合起来用一条命令表达。在rewrite期间的写操作会保存在内存的rewrite buffer中,rewrite成功后这些操作也会复制到临时文件中,在最后临时文件会代替AOF文件。
    以上在AOF打开的情况下,如果AOF是关闭的,那么rewrite操作可以通过bgrewriteaof命令来进行。

    Redis AOF流程

    1. Redis Server启动,如果AOF机制打开那么初始化AOF状态,并且如果存在AOF文件,读取AOF文件。
    2. 随着Redis不断接受命令,每个写命令都被添加到AOF文件,AOF文件膨胀到需要rewrite时又或者接收到客户端的bgrewriteaof命令。
    3. fork出一个子进程进行rewrite,而父进程继续接受命令,现在的写操作命令都会被额外添加到一个aof_rewrite_buf_blocks缓冲中。
    4. 当子进程rewrite结束后,父进程收到子进程退出信号,把aof_rewrite_buf_blocks的缓冲添加到rewrite后的文件中,然后切换AOF的文件fd。rewrite任务完成,继续第二个步骤。

    关键点

      • 由于写操作通常是有缓冲的,所以有可能AOF操作并没有写到硬盘中,一般可以通过fsync()来强制输出到硬盘中。而fsync()的频率可以通过配置文件中的flush策略来指定,可以选择每次事件循环写操作都强制fsync或者每秒fsync至少运行一次。
      • 当rewrite子进程开始后,父进程接受到的命令会添加到aof_rewrite_buf_blocks中,使得rewrite成功后,将这些命令添加到新文件中。在rewrite过程中,原来的AOF也可以选择是不是继续添加,由于存在性能上的问题,在rewrite过程中,如果fsync()继续执行,会导致IO性能受损影响Redis性能。所以一般情况下rewrite期间禁止fsync()到旧AOF文件。这策略可以在配置文件中修改。
      • 在rewrite结束后,在将新rewrite文件重命名为配置中指定的文件时,如果旧AOF存在,那么会unlink掉旧文件。这是就存在一个问题,处理rewrite文件迁移的是主线程,rename(oldpath, newpath)过程会覆盖旧文件,这是rename会unlink(oldfd),而unlink操作会导致block主线程。这时,我们就需要类似libeio(http://software.schmorp.de/pkg/libeio.html)这样的库去进行异步的底层IO。作者在bio.c有一个类似的机制,通过创建新线程来进行异步操作。
      • ==========================================================================================================
      • 自动的bgrewriteaof

        为了避免aof文件过大,我们会周期性的做bgrewriteaof来重整aof文件。以前我们会额外的配置crontab在业务低峰期执行这个命令,这额外的增加一个workaroud的脚本任务在大集群里是很糟糕的,不易检查,出错无法即时发现。

        于是这个自动bgrewriteaof功能被直接加到redis的内部。首先对于aof文件,server对象添加一个字段来记录aof文件的大小server.appendonly_current_size,每次aof发生变化都会维护这个字段。

        aof.c
        =================
        116     nwritten = write(server.appendfd,server.aofbuf,sdslen(server.aofbuf));
        .....
        128     server.appendonly_current_size += nwritten;

        bgrewriteaof完毕或者实例启动载入aof数据后也会调用aofUpdateCurrentSize这个函数维护这个字段,同时会记录下此时的aof文件的大小server.auto_aofrewrite_base_size作为基准值,用于接下来判断aof增长率。

        aof.c
        =================
        385     aofUpdateCurrentSize();
        386     server.auto_aofrewrite_base_size = server.appendonly_current_size;

        有了当前值和基准值我们就可以判断aof文件的增长情况。另外还需要配置两个参数来判断是否需要自动触发bgrewriteaof。

        redis.h
        ===============
        int auto_aofrewrite_perc; /* Rewrite AOF if % growth is > M and... */
        off_t auto_aofrewrite_min_size; /* the AOF file is at least N bytes. */

        auto_aofrewrite_perc: aof文件的大小超过基准百分之多少后触发bgrewriteaof。默认这个值设置为100,意味着当前aof是基准大小的两倍的时候触发bgrewriteaof。把它设置为0可以禁用自动触发的功能。
        auto_aofrewrite_min_size: 当前aof文件大于多少字节后才触发。避免在aof较小的时候无谓行为。默认大小为64mb。
        两个参数都是可以在conf里静态配置,或者通过config set来动态修改的。

        redis 127.0.0.1:6379> config get auto-aof-rewrite-percentage
        1) "auto-aof-rewrite-percentage"
        2) "100"
        redis 127.0.0.1:6379> config get auto-aof-rewrite-min-size
        1) "auto-aof-rewrite-min-size"
        2) "1048576"
        redis 127.0.0.1:6379> config get auto-aof-rewrite-min-size
        1) "auto-aof-rewrite-min-size"
        2) "1048576"
        redis 127.0.0.1:6379> config set auto-aof-rewrite-percentage 200
        OK
        redis 127.0.0.1:6379> config set auto-aof-rewrite-min-size 10485760
        OK

        然后就是触发检查的主逻辑,serverCron时间事件中每次都会检查现有状态和参数来判断是否需要启动bgrewriteaof。

        redis.c
        ===============
        635          if (server.bgsavechildpid == -1 &&
         636              server.bgrewritechildpid == -1 &&
         637              server.auto_aofrewrite_perc &&
         638              server.appendonly_current_size > server.auto_aofrewrite_min_size)
         639          {
         640             long long base = server.auto_aofrewrite_base_size ?
         641                             server.auto_aofrewrite_base_size : 1;
         642             long long growth = (server.appendonly_current_size*100/base) - 100;
         643             if (growth >= server.auto_aofrewrite_perc) {
         644                 redisLog(REDIS_NOTICE,"Starting automatic rewriting of AOF on %lld%% growth",growth);
         645                 rewriteAppendOnlyFileBackground();
         646             }
         647         }

        以上代码显示,如果aof文件增长百分率growth大于auto_aofrewrite_perc,则自动的触发后一个bgrewriteaof。

        延迟bgrewriteaof

        这是个小的改进,手动触发的bgrewriteaof的时候如果同时存在bgsave在备份,会推迟这次操走的事件,设置server.aofrewrite_scheduled=1,待到bgsave结束后的下一次serverCron里才会触发。

        设置aofrewrite_scheduled=1

        aof.c
        706 void bgrewriteaofCommand(redisClient *c) {
        707     if (server.bgrewritechildpid != -1) {
        708         addReplyError(c,"Background append only file rewriting already in progress");
        709     } else if (server.bgsavechildpid != -1) {
        710         server.aofrewrite_scheduled = 1;
        711         addReplyStatus(c,"Background append only file rewriting scheduled");
        712     } else if (rewriteAppendOnlyFileBackground() == REDIS_OK) {
        713         addReplyStatus(c,"Background append only file rewriting started");
        714     } else {
        715         addReply(c,shared.err);
        716     }
        717 }

        触发bgrewriteaof

        redis.c
         598     if (server.bgsavechildpid == -1 && server.bgrewritechildpid == -1 &&
         599         server.aofrewrite_scheduled)
         600     {
         601         rewriteAppendOnlyFileBackground();
         602    }

        总结:

        rewrite机制:aof里存放了所有的redis 操作指令,当aof文件达到一定条件或者手动

        bgrewriteaof命令都可以触发rewrite

        rewrite之后aof文件会保存keys的最后的状态,清除掉之前冗余的,来缩小这个文件。

        自动触发的条件:

         long long growth =(server.appendonly_current_size*100/base) - 100;
         if (growth >=server.auto_aofrewrite_perc)

         

        在配置文件里设置过:

        auto-aof-rewrite-percentage 100 (当前写入日志文件的大小超过上一次rewrite之后的文件大小的百分100就是2倍时触发Rewrite

  • 相关阅读:
    项目管理理论与实践(6)——利用Excel制作项目文档的设计技巧
    项目管理理论与实践(5)——UML应用(下)
    【工具推荐】ELMAH——可插拔错误日志工具
    ExtJs实践(1)——Ext.extend的用法
    设计一套基于NHibernate二级缓存的MongoDB组件(上)
    ExtJs实践(3)——xtype名称与控件对应
    项目管理理论与实践(2)——软件需求分析
    【培训稿件】构建WCF面向服务的应用程序(包含ppt,源代码)
    ExtJs实践(5)——解决在GridPanel中使用bbar或者tbar的分页条的宽度自适应问题
    项目管理理论与实践(3)——如何进行有效的项目管理
  • 原文地址:https://www.cnblogs.com/lyh233/p/13196231.html
Copyright © 2011-2022 走看看