• 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();
            }
    }
    
  • 相关阅读:
    Yii Framework 开发教程: 总结
    code first 如何创建索引字段
    Entity Framework 使用 Left Join
    如何查看IIS并发连接数
    c# List<int> 转 string 以及 string [] 转 List<int>
    linq中如何在join中指定多个条件
    关于学习的感悟
    c# 的访问修饰符是private 还是 internal?
    C# 与 .Net 3.5 高级程序设计(第四版) 读书笔记(一)
    解决Excel 提示向程序发送命令时出现错误
  • 原文地址:https://www.cnblogs.com/gaogao67/p/15213992.html
Copyright © 2020-2023  润新知