• Redis源码分析RDB持久化


    RDB持久化:

    一、引言:

    命令 功能
    SAVE 创建RDB文件;阻塞服务器进程
    BGSAVE 创建RDB文件;派生子进程,所以不阻塞服务器进程

    ​ RDB文件以二进制形式存储,通过保存服务器中所有非空数据库以及其中的所有键值对(数据库状态)实现持久化;


    二、RDB文件的创建:

    ​ BGSAVE和SAVE命令创建RDB文件都是通过rdbSave函数实现的,直接分析这个函数:

    /* Save the DB on disk. Return REDIS_ERR on error, REDIS_OK on success */
    int rdbSave(char *filename) {
        dictIterator *di = NULL;
        dictEntry *de;
        char tmpfile[256];
        char magic[10];
        int j;
        long long now = mstime();
        FILE *fp;
        rio rdb;
        uint64_t cksum;
    
        /* 创建文件 */
        snprintf(tmpfile,256,"temp-%d.rdb", (int) getpid());
        fp = fopen(tmpfile,"w");
        if (!fp) {
            redisLog(REDIS_WARNING, "Failed opening .rdb for saving: %s",
                strerror(errno));
            return REDIS_ERR;
        }
    
        rioInitWithFile(&rdb,fp);
        if (server.rdb_checksum)
            rdb.update_cksum = rioGenericUpdateChecksum;
        /* RDB文件头部几个字节REDIS+version,程序可以在载入时快速判断是否时RDB文件 */
        snprintf(magic,sizeof(magic),"REDIS%04d",REDIS_RDB_VERSION);
        if (rdbWriteRaw(&rdb,magic,9) == -1) goto werr;
    
        for (j = 0; j < server.dbnum; j++) {
            redisDb *db = server.db+j;
            dict *d = db->dict;
            if (dictSize(d) == 0) continue;
            di = dictGetSafeIterator(d);
            if (!di) {
                fclose(fp);
                return REDIS_ERR;
            }
    
            /* Write the SELECT DB opcode */
            if (rdbSaveType(&rdb,REDIS_RDB_OPCODE_SELECTDB) == -1) goto werr;
            if (rdbSaveLen(&rdb,j) == -1) goto werr;
    
            /* Iterate this DB writing every entry */
            while((de = dictNext(di)) != NULL) {
                sds keystr = dictGetKey(de);
                robj key, *o = dictGetVal(de);
                long long expire;
                
                initStaticStringObject(key,keystr);
                expire = getExpire(db,&key);
                if (rdbSaveKeyValuePair(&rdb,&key,o,expire,now) == -1) goto werr;
            }
            dictReleaseIterator(di);
        }
        di = NULL; /* So that we don't release it again on error. */
    
        /* EOF opcode */
        if (rdbSaveType(&rdb,REDIS_RDB_OPCODE_EOF) == -1) goto werr;
    
        /* CRC64 checksum. It will be zero if checksum computation is disabled, the
         * loading code skips the check in this case. */
        cksum = rdb.cksum;
        memrev64ifbe(&cksum);
        rioWrite(&rdb,&cksum,8);
    
        /* Make sure data will not remain on the OS's output buffers */
        fflush(fp);
        fsync(fileno(fp));
        fclose(fp);
    
        /* Use RENAME to make sure the DB file is changed atomically only
         * if the generate DB file is ok. */
        if (rename(tmpfile,filename) == -1) {
            redisLog(REDIS_WARNING,"Error moving temp DB file on the final destination: %s", strerror(errno));
            unlink(tmpfile);
            return REDIS_ERR;
        }
        redisLog(REDIS_NOTICE,"DB saved on disk");
        server.dirty = 0;
        server.lastsave = time(NULL);
        server.lastbgsave_status = REDIS_OK;
        return REDIS_OK;
    
    werr:
        fclose(fp);
        unlink(tmpfile);
        redisLog(REDIS_WARNING,"Write error saving DB on disk: %s", strerror(errno));
        if (di) dictReleaseIterator(di);
        return REDIS_ERR;
    }
    
    • L67~L69:参考[1];

    三、RDB文件载入:

    ​ 类似RDB文件的创建,不赘述;


    参考:

    1. 函数sync、fsync与fdatasync的总结整理(必看篇)_Linux_脚本之家 (jb51.net)
  • 相关阅读:
    JavaScript 初学者应知的 24 条最佳实践
    利用函数的惰性载入提高 javascript 代码性能
    Android多线程研究(9)——线程锁Lock
    Android多线程研究(8)——Java5中Futrue获取线程返回结果
    MySQL 5.6初始配置调优
    asp.net文件夹上传下载组件
    java文件夹上传下载组件
    web文件上传下载组件
    jsp文件上传下载组件
    flash文件上传下载组件
  • 原文地址:https://www.cnblogs.com/macguz/p/15873016.html
Copyright © 2020-2023  润新知