• redis.conf之save配置项解读


    配置示例:

    save 900 1

    save 300 10

    save 60 3600

     

    配置解读:

    1) “save 900 1”表示如果900秒内至少1key发生变化(新增、修改和删除),则重写rdb文件;

    2) “save 300 10”表示如果每300秒内至少10key发生变化(新增、修改和删除),则重写rdb文件;

    3) “save 60 3600”表示如果每60秒内至少10000key发生变化(新增、修改和删除),则重写rdb文件。

     

    作用:

    控制什么时候生成rdb文件(快照,也可叫Checkpoint,即检查点)。

     

    进程启动的时候,会将每一行save读进到类型为struct saveparam的数组中。这个没有排序,依在redis.conf中的先后顺序。在检查时,只要满足就不会再检查下一条规则。

     

    配置策略:

    如果同时开启了aof,则可考虑将save的参数调大一点,以减少写rdb带来的压力。实际上如果开启了aofredis在启动时只会读取aof文件,而不会读取rdb文件:

    // Function called at startup to load RDB or AOF file in memory.

    void loadDataFromDisk(void) {    

        if (server.aof_state == AOF_ON) {      

            // 允许空的aof文件,

            // 如果读取aof文件出错,则调用exit(1)直接退出进程

            if (loadAppendOnlyFile(server.aof_filename) == C_OK)

                serverLog(LL_NOTICE,"DB loaded from append only file: %.3f seconds",(float)(ustime()-start)/1000000);

        } else {

             if (rdbLoad(server.rdb_filename,&rsi) == C_OK) {

        }

    }

     

    // #define AOF_OFF 0             /* AOF is off */

    // #define AOF_ON 1              /* AOF is on */

    void loadServerConfigFromString(char *config) {

        。。。。。。

        } else if (!strcasecmp(argv[0],"appendonly") && argc == 2) {

            int yes;

            if ((yes = yesnotoi(argv[1])) == -1) {

                err = "argument must be 'yes' or 'no'"; goto loaderr;

            }

             server.aof_state = yes ? AOF_ON : AOF_OFF;

        }

        。。。。。。

    }

     

    int yesnotoi(char *s) {

        if (!strcasecmp(s,"yes")) return 1;

        else if (!strcasecmp(s,"no")) return 0;

        else return -1;

    }

     

    调用顺序:

    main()/server.c ->

    aeMain()/ae.c -> while (!stop) { aeProcessEvents()/ae.c } ->

    serverCron()/server.c -> rdbSaveBackground()/server.c

     

    注:

    aeProcessEvents可看作是个epoll_wait调用,在Linux上实际正是epoll_wait调用,而在Solaris上则是port_getn调用。

     

    相关源代码:

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

        。。。。。。

        // Check if a background saving or AOF rewrite in progress terminated.

        // 如果已有rdb和aof进程,检查进程是否已退出。

        // 如果已退出,则会善后处理,否则什么也不做,等待下一次循环时再次检查

        if (server.rdb_child_pid != -1 ||

            server.aof_child_pid != -1 ||

             ldbPendingChildren()) {

            int statloc;

            pid_t pid;

            if ((pid = wait3(&statloc,WNOHANG,NULL)) != 0) {

                int exitcode = WEXITSTATUS(statloc);

                。。。。。。

            }

        } else {

            // If there is not a background saving/rewrite

            // in progress check if we have to save/rewrite now.

            // 按在redis.conf中定义的顺序依次遍历每一行配置项

            // 最终是否进行写rdb操作(即生成快照文件),不仅由redis.conf

            // 中的配置项决定,还要看上一次操作的结果和状态。

            for (j = 0; j < server.saveparamslen; j++) {

                struct saveparam *sp = server.saveparams+j;

                // Save if we reached the given amount of changes,

                // the given amount of seconds, and if the latest bgsave was

                // successful or if, in case of an error, at least

                // CONFIG_BGSAVE_RETRY_DELAY seconds already elapsed.

                // CONFIG_BGSAVE_RETRY_DELAY(5): Wait a few secs before trying again.

                if (server.dirty >= sp->changes &&

                    server.unixtime-server.lastsave > sp->seconds &&

                    (server.unixtime-server.lastbgsave_try>CONFIG_BGSAVE_RETRY_DELAY ||

                     server.lastbgsave_status == C_OK)) {

                    serverLog(LL_NOTICE,"%d changes in %d seconds. Saving...",

                        sp->changes, (int)sp->seconds);

                    rdbSaveInfo rsi, *rsiptr;

                    rsiptr = rdbPopulateSaveInfo(&rsi);

                    rdbSaveBackground(server.rdb_filename,rsiptr);

                    break; // 遇到一条满足的即结束处理,因为已没有必要判断是否满足下一条配置规则

                }

            }

        }

        。。。。。。     

    }

     

    // rdb.c

    int rdbSaveBackground(char *filename, rdbSaveInfo *rsi) {

        。。。。。。

        server.lastbgsave_try = time(NULL);

        。。。。。。

        // 创建写rdb的子进程

        if ((childpid = fork()) == 0) {

            redisSetProcTitle("redis-rdb-bgsave");

            retval = rdbSave(filename,rsi);

        }

        。。。。。。

    }

     

    /* Save the DB on disk. Return C_ERR on error, C_OK on success. */

    // rdb.c

    // rdbSave调用rdbSaveRio将数据写入到rdb文件中

    int rdbSave(char *filename, rdbSaveInfo *rsi) {

        。。。。。。

        // 写rdb文件

        if (rdbSaveRio(&rdb,&error,RDB_SAVE_NONE,rsi) == C_ERR) {

            errno = error;

            goto werr;

        }

        。。。。。。

        serverLog(LL_NOTICE,"DB saved on disk");

        server.dirty = 0;

        server.lastsave = time(NULL);

        server.lastbgsave_status = C_OK;

        return C_OK;

    }

     

    /* Produces a dump of the database in RDB format sending it to the specified

     * Redis I/O channel. On success C_OK is returned, otherwise C_ERR

     * is returned and part of the output, or all the output, can be

     * missing because of I/O errors.

     *

     * When the function returns C_ERR and if 'error' is not NULL, the

     * integer pointed by 'error' is set to the value of errno just after the I/O

     * error. */

    int rdbSaveRio(rio *rdb, int *error, int flags, rdbSaveInfo *rsi) {

        for (j = 0; j < server.dbnum; j++) {

            。。。。。。

            // Iterate this DB writing every entry

            while((de = dictNext(di)) != NULL) {

                。。。。。。

                // 将一对对KV写入到rdb文件

                if (rdbSaveKeyValuePair(rdb,&key,o,expire) == -1) goto werr;            

                。。。。。。

            }

        }

        。。。。。。

    werr:

        if (error) *error = errno;

        if (di) dictReleaseIterator(di);

        return C_ERR;

    }

     

    // 以SADD命令为例,所有写操作,均会修改dirty 的值

    void saddCommand(client *c) {

        。。。。。。

        for (j = 2; j < c->argc; j++) {

            if (setTypeAdd(set,c->argv[j]->ptr)) added++;

        }

        。。。。。。

        server.dirty += added;

        addReplyLongLong(c,added);

    }

  • 相关阅读:
    C#中创建Android项目
    C#中创建Android项目
    在C#中获取当前屏幕的分辨率的方法
    数据库面试
    计算机网络面试
    linux面试
    Java使用递归检索文件个数
    二分查找
    富途证券面经(一面挂)
    Mysql
  • 原文地址:https://www.cnblogs.com/aquester/p/10233920.html
Copyright © 2020-2023  润新知