Redis学习--AOF日志重写触发场景

AOFRewrite触发场景

AOFRewrite触发场景:

  • 执行BGREWRITEAOF命令重写AOF日志时触发
  • 使用CONFIG SET命令开启AOF日志时触发
  • 当AOF日志超过基准大小的特定百分比时(参数auto-aof-rewrite-percentage控制)时触发。

执行BGREWRITEAOF命令时触发

当执行BGREWRITEAOF命令时,会调用bgrewriteaofCommand来进行AOF日志重写。

void bgrewriteaofCommand(client *c) {
    if (server.aof_child_pid != -1) {
        addReplyError(c,"Background append only file rewriting already in progress");
    } else if (server.rdb_child_pid != -1) {
        server.aof_rewrite_scheduled = 1;
        addReplyStatus(c,"Background append only file rewriting scheduled");
    } else if (rewriteAppendOnlyFileBackground() == C_OK) {
        addReplyStatus(c,"Background append only file rewriting started");
    } else {
        addReply(c,shared.err);
    }
}

使用CONFIG SET命令开启AOF日志时触发

当使用config set appendonly yes命令来开启AOF日志时,会调用startAppendOnly来进行AOF日志重写。

/* Called when the user switches from "appendonly no" to "appendonly yes"
 * at runtime using the CONFIG command. */
int startAppendOnly(void) {
    char cwd[MAXPATHLEN]; /* Current working dir path for error messages. */
    int newfd;

    newfd = open(server.aof_filename,O_WRONLY|O_APPEND|O_CREAT,0644);
    serverAssert(server.aof_state == AOF_OFF);
    if (newfd == -1) {
        char *cwdp = getcwd(cwd,MAXPATHLEN);

        serverLog(LL_WARNING,
            "Redis needs to enable the AOF but can't open the "
            "append only file %s (in server root dir %s): %s",
            server.aof_filename,
            cwdp ? cwdp : "unknown",
            strerror(errno));
        return C_ERR;
    }
    if (server.rdb_child_pid != -1) {
        server.aof_rewrite_scheduled = 1;
        serverLog(LL_WARNING,"AOF was enabled but there is already a child process saving an RDB file on disk. An AOF background was scheduled to start when possible.");
    } else {
        /* If there is a pending AOF rewrite, we need to switch it off and
         * start a new one: the old one cannot be reused because it is not
         * accumulating the AOF buffer. */
        if (server.aof_child_pid != -1) {
            serverLog(LL_WARNING,"AOF was enabled but there is already an AOF rewriting in background. Stopping background AOF and starting a rewrite now.");
            killAppendOnlyChild();
        }
        if (rewriteAppendOnlyFileBackground() == C_ERR) {
            close(newfd);
            serverLog(LL_WARNING,"Redis needs to enable the AOF but can't trigger a background AOF rewrite operation. Check the above logs for more info about the error.");
            return C_ERR;
        }
    }
    /* We correctly switched on AOF, now wait for the rewrite to be complete
     * in order to append data on disk. */
    server.aof_state = AOF_WAIT_REWRITE;
    server.aof_last_fsync = server.unixtime;
    server.aof_fd = newfd;
    return C_OK;
}

当Redis实例故障恢复时,如果非sentinel模式,会尝试从本地存储上加载数据。

int main(int argc, char **argv) {
    if (!server.sentinel_mode) {
        moduleLoadFromQueue();
        InitServerLast();
        loadDataFromDisk();
    } else {
        InitServerLast();
        sentinelIsRunning();
    }
}

在加载过程中,如果开启AOF日志,只会加载AOF文件。如果未开启AOF日志,则会尝试加载RDB文件。

/* Function called at startup to load RDB or AOF file in memory. */
void loadDataFromDisk(void) {
    long long start = ustime();
    if (server.aof_state == AOF_ON) {
        if (loadAppendOnlyFile(server.aof_filename) == C_OK)
            serverLog(LL_NOTICE,
                      "DB loaded from append only file: %.3f seconds",
                      (float)(ustime()-start)/1000000);
    } else {
        rdbSaveInfo rsi = RDB_SAVE_INFO_INIT;
        if (rdbLoad(server.rdb_filename,&rsi) == C_OK) {
            serverLog(LL_NOTICE,"DB loaded from disk: %.3f seconds",
                (float)(ustime()-start)/1000000);

            /* Restore the replication ID / offset from the RDB file. */
            if ((server.masterhost ||
                (server.cluster_enabled &&
                nodeIsSlave(server.cluster->myself))) &&
                rsi.repl_id_is_set &&
                rsi.repl_offset != -1 &&
                /* Note that older implementations may save a repl_stream_db
                 * of -1 inside the RDB file in a wrong way, see more
                 * information in function rdbPopulateSaveInfo. */
                rsi.repl_stream_db != -1)
            {
                memcpy(server.replid,rsi.repl_id,sizeof(server.replid));
                server.master_repl_offset = rsi.repl_offset;
                /* If we are a slave, create a cached master from this
                 * information, in order to allow partial resynchronizations
                 * with masters. */
                replicationCacheMasterUsingMyself();
                selectDb(server.cached_master,rsi.repl_stream_db);
            }
        } else if (errno != ENOENT) {
            serverLog(LL_WARNING,"Fatal error loading the DB: %s. Exiting.",strerror(errno));
            exit(1);
        }
    }
}

在Redis实例运行过程中,如果开启AOF日志且AOF日志中仅存放开启AOF日志后的AOF日志,那么无法保证Redis故障恢复时AOF文件中包含所有数据,因此需要在开启AOF日志时进行一次AOF Rewrite操作。

PS:如果开启AOF日志后但AOF Rewrite操作未完成前发生故障恢复会丢失数据。

AOF日志增长率超过阈值时触发

在定期执行的serverCron函数中,会检查AOF日志文件与基准文件的增长比例,当增长比例超过参数auto-aof-rewrite-percentage阈值后触发AOF日志重写:

int serverCron(struct aeEventLoop *eventLoop, long long id, void *clientData) {

	/* Trigger an AOF rewrite if needed. */
	if (server.aof_state == AOF_ON &&
		server.rdb_child_pid == -1 &&
		server.aof_child_pid == -1 &&
		server.aof_rewrite_perc &&
		server.aof_current_size > server.aof_rewrite_min_size)
	{
		long long base = server.aof_rewrite_base_size ?
			server.aof_rewrite_base_size : 1;
		long long growth = (server.aof_current_size*100/base) - 100;
		if (growth >= server.aof_rewrite_perc) {
			serverLog(LL_NOTICE,"Starting automatic rewriting of AOF on %lld%% growth",growth);
			rewriteAppendOnlyFileBackground();
		}
	}

}

参数auto-aof-rewrite-percentage初始化:

## 设置AOF日志重写的触发阈值,默认为100,即当前AOF日志是基准AOF日志大小的两倍时触发重写。
#define AOF_REWRITE_PERC  100

## 通过参数auto-aof-rewrite-percentage或预定义变量AOF_REWRITE_PERC来初始化server.aof_rewrite_perc
rewriteConfigNumericalOption(
    state,
    "auto-aof-rewrite-percentage",
    server.aof_rewrite_perc,AOF_REWRITE_PERC);

变量server.aof_rewrite_base_size初始化:

  • 当Redis实例故障恢复时,使用loadAppendOnlyFile来恢复数据,并用当前的AOF日志文件作为基准文件。
  • 当Redis实例AOF日志重写完成后,使用backgroundRewriteDoneHandler来清理操作,并用当前AOF日志文件作为基准文件。
/* Replay the append log file. On success C_OK is returned. On non fatal
 * error (the append only file is zero-length) C_ERR is returned. On
 * fatal error an error message is logged and the program exists. */
int loadAppendOnlyFile(char *filename) {

loaded_ok: /* DB loaded, cleanup and return C_OK to the caller. */
    fclose(fp);
    freeFakeClient(fakeClient);
    server.aof_state = old_aof_state;
    stopLoading();
    aofUpdateCurrentSize();
    server.aof_rewrite_base_size = server.aof_current_size;
    server.aof_fsync_offset = server.aof_current_size;
    return C_OK;

}

/* A background append only file rewriting (BGREWRITEAOF) terminated its work.
 * Handle this. */
void backgroundRewriteDoneHandler(int exitcode, int bysignal) {
    if (server.aof_fd == -1) {
            /* AOF disabled, we don't need to set the AOF file descriptor
             * to this new file, so we can close it. */
            close(newfd);
        } else {
            /* AOF enabled, replace the old fd with the new one. */
            oldfd = server.aof_fd;
            server.aof_fd = newfd;
            if (server.aof_fsync == AOF_FSYNC_ALWAYS)
                redis_fsync(newfd);
            else if (server.aof_fsync == AOF_FSYNC_EVERYSEC)
                aof_background_fsync(newfd);
            server.aof_selected_db = -1; /* Make sure SELECT is re-issued */
            aofUpdateCurrentSize();
            server.aof_rewrite_base_size = server.aof_current_size;
            server.aof_fsync_offset = server.aof_current_size;

            /* Clear regular AOF buffer since its contents was just written to
             * the new AOF from the background rewrite buffer. */
            sdsfree(server.aof_buf);
            server.aof_buf = sdsempty();
        }
}
posted @ 2021-09-01 11:44  TeyGao  阅读(411)  评论(0编辑  收藏  举报