本文取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文件过程
挺复杂的,后面继续研究
/* 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 */// 关闭网络连接 fdcloseListeningSockets(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;// 关闭字典自动 rehashupdateDictResizePolicy();/* 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 */
}