Redis AOF源码解析

本文取3.0版本分析(各个版本差异很大,4.0以上才有aof和rdb混合模式)

触发时机

        1、bgrewriteaofCommand函数触发,即在Redis server服务上运行bgrewriteaof命令。

        1-1、当前已经有 AOF 重写的子进程正在执行,重复执行bgrewriteaof命令,是不会执行bgrewriteaof命令。

        1-2、执行rewriteAppendOnlyFileBackground函数。

        2、rewriteAppendOnlyFileBackground函数触发:

        2-1、当前没有aof子进程和rdb子进程,但是存在着等待执行任务调度的aof命令,只有到了执行任务调度aof命令的情况下,才会触发aof重写。

        2-2、当前没有aof子进程和rdb子进程,但是设置了aof阈值的情况,如果当前aof文件超过了预留的阈值,则会触发aof重写。

        3、开启Redis server aof配置功能

        3-1、当用户在运行时使用config命令,从 appendonly no 切换到 appendonly yes 时执行。

        3-2、执行rewriteAppendOnlyFileBackground函数。

        4、主从复制函数readSyncBulkPayload,startAppendOnly函数

        4-1、主从复制开启aof配置功能下,如果有rdb子进程,那么等待rdb子进程处理完后,再通过任务调度执行aof持久化,如果有aof进程,强制kill掉正在执行的aof子进程,重新生成aof持久化文件。

生成aof文件过程

1、6.0版本采用Reactor事件驱动为管道(aof_pipe_write_data_to_child)注册了一个重写事件。

//aof_pipe_write_data_to_child管道描述符,通过监听aof_pipe_write_data_to_child来实现管道通讯,并且回调aofChildWriteDiffData重写aof事件。
aeCreateFileEvent(server.el, server.aof_pipe_write_data_to_child, AE_WRITABLE, aofChildWriteDiffData, NULL);

//重写事件
aofChildWriteDiffData

  3.0版本没有使用事件驱动,会在redis server启动时分配一个buffer来操作aof文件,并且通过打开open文件来实现aof重写,实现依然是主进程fork子进程来实现管道通信。

/* This is how rewriting of the append only file in background works:
 * 
 * 以下是后台重写 AOF 文件(BGREWRITEAOF)的工作步骤:
 *
 * 1) The user calls BGREWRITEAOF
 *    用户调用 BGREWRITEAOF
 *
 * 2) Redis calls this function, that forks():
 *    Redis 调用这个函数,它执行 fork() :
 *
 *    2a) the child rewrite the append only file in a temp file.
 *        子进程在临时文件中对 AOF 文件进行重写
 *
 *    2b) the parent accumulates differences in server.aof_rewrite_buf.
 *        父进程将新输入的写命令追加到 server.aof_rewrite_buf 中
 *
 * 3) When the child finished '2a' exists.
 *    当步骤 2a 执行完之后,子进程结束
 *
 * 4) The parent will trap the exit code, if it's OK, will append the
 *    data accumulated into server.aof_rewrite_buf into the temp file, and
 *    finally will rename(2) the temp file in the actual file name.
 *    The the new file is reopened as the new append only file. Profit!
 *
 *    父进程会捕捉子进程的退出信号,
 *    如果子进程的退出状态是 OK 的话,
 *    那么父进程将新输入命令的缓存追加到临时文件,
 *    然后使用 rename(2) 对临时文件改名,用它代替旧的 AOF 文件,
 *    至此,后台 AOF 重写完成。
 */
int rewriteAppendOnlyFileBackground(void) {
    pid_t childpid;
    long long start;

    // 已经有进程在进行 AOF 重写了
    if (server.aof_child_pid != -1) return REDIS_ERR;

    // 记录 fork 开始前的时间,计算 fork 耗时用
    start = ustime();

    if ((childpid = fork()) == 0) {
        char tmpfile[256];

        /* Child */

        // 关闭网络连接 fd
        closeListeningSockets(0);

        // 为进程设置名字,方便记认
        redisSetProcTitle("redis-aof-rewrite");

        // 创建临时文件,并进行 AOF 重写
        snprintf(tmpfile,256,"temp-rewriteaof-bg-%d.aof", (int) getpid());
        if (rewriteAppendOnlyFile(tmpfile) == REDIS_OK) {
            size_t private_dirty = zmalloc_get_private_dirty();

            if (private_dirty) {
                redisLog(REDIS_NOTICE,
                    "AOF rewrite: %zu MB of memory used by copy-on-write",
                    private_dirty/(1024*1024));
            }
            // 发送重写成功信号
            exitFromChild(0);
        } else {
            // 发送重写失败信号
            exitFromChild(1);
        }
    } else {
        /* Parent */
        // 记录执行 fork 所消耗的时间
        server.stat_fork_time = ustime()-start;

        if (childpid == -1) {
            redisLog(REDIS_WARNING,
                "Can't rewrite append only file in background: fork: %s",
                strerror(errno));
            return REDIS_ERR;
        }

        redisLog(REDIS_NOTICE,
            "Background append only file rewriting started by pid %d",childpid);

        // 记录 AOF 重写的信息
        server.aof_rewrite_scheduled = 0;
        server.aof_rewrite_time_start = time(NULL);
        server.aof_child_pid = childpid;

        // 关闭字典自动 rehash
        updateDictResizePolicy();

        /* We set appendseldb to -1 in order to force the next call to the
         * feedAppendOnlyFile() to issue a SELECT command, so the differences
         * accumulated by the parent into server.aof_rewrite_buf will start
         * with a SELECT statement and it will be safe to merge. 
         *
         * 将 aof_selected_db 设为 -1 ,
         * 强制让 feedAppendOnlyFile() 下次执行时引发一个 SELECT 命令,
         * 从而确保之后新添加的命令会设置到正确的数据库中
         */
        server.aof_selected_db = -1;
        replicationScriptCacheFlush();
        return REDIS_OK;
    }
    return REDIS_OK; /* unreached */
}
  • 11
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值