结合redis设计与实现的redis源码学习-12-RDB持久化(rdb.h/rio.h)

来源:互联网 发布:永利国际中心 商业数据 编辑:程序博客网 时间:2024/05/18 01:10

因为redis是一个内存数据库,他将自己的数据库状态存储在内存中,一旦服务器锦城推出,服务器中的数据库状态也会消失不见。为了解决这个问题,redis提供了RDB持久化功能,这个功能可以将redis在内存中的数据库状态保存在磁盘里,避免数据意外丢失。
RDB持久化既可以手动执行,也可以根据服务器配置定期执行,该功能可以将某个时间点上的数据库状态保存到一个RDB文件中,这个文件是一个经过压缩的二进制文件,通过改文件可以还原生成RDB文件时的数据库状态。
只要这个文件存在,redis就可以用它来还原数据库状态。
rdb的功能实现在rdb.h/rdb.c中。

rdb文件的创建和载入

与rdb持久化有关的命令有两个:save和bgsave。
save命令会阻塞服务器进程,指导rdb文件创建完成。
bgsave会创建一个子进程来负责创建rdb文件,服务器进程继续处理命令。
redis没有为载入rdb文件设置命令,因为在服务器启动时检测到rdb文件他就会自动载入。

rdb持久化的自动间隔性保存

用户可以通过设置服务器的save选项来让服务器每个一段时间自动执行一次bgsave命令。可以通过save选项设置多个保存条件,只要其中一个满足,服务器就会自动执行bgsave命令。
如:save 300 10 //只要在300秒内执行了至少10次操作就会执行bgsave
如果用户没有指定save选项,服务器默认的选项为:save 900 1,save 300 10, save 60 10000.
服务器会根据save选项所设置的保存条件,设置服务器状态redisServer结构的saveparams属性。

struct saveparam {    time_t seconds;//秒数    int changes;//修改数};

redisServer结构中还维护了一个dirty计数器和lastsave属性,当这两个条件与saveparam中的数据匹配时就执行bgsave,服务器会周期性的检测是否满足条件:

struct redisServer{    //...    long long dirty;//修改计数器;    time_t lastsave;//上一次保存的时间    //...};

rdb文件的格式

rdb文件使用redis自定义的tlv二进制格式。

看代码

rio.h是文件io的操作模块,这里只对rio的头文件进行说明。

#ifndef __REDIS_RIO_H#define __REDIS_RIO_H#include <stdio.h>#include <stdint.h>#include "sds.h"//rio结构体struct _rio {    /* Backend functions. Since this functions do not tolerate short writes or reads the return value is simplified to: zero on error, non zero on complete success. 后端功能,由于这个函数不允许短写或读操作,返回值被简化为:错误为0,完成非0*/    size_t (*read)(struct _rio *, void *buf, size_t len);    size_t (*write)(struct _rio *, const void *buf, size_t len);    off_t (*tell)(struct _rio *);    int (*flush)(struct _rio *);    /* The update_cksum method if not NULL is used to compute the checksum of all the data that was read or written so far. The method should be designed so that can be called with the current checksum, and the buf and len fields pointing to the new block of data to add to the checksum computation. 用于计算读取或者写入的所有数据的校验和,被设计成可以用但前的校验和来调用,buf和len字段指向新的数据块来添加校验和计算*/    void (*update_cksum)(struct _rio *, const void *buf, size_t len);    /* The current checksum 当前校验和*/    uint64_t cksum;    /* number of bytes read or written 读写的字节数*/    size_t processed_bytes;    /* maximum single read or write chunk size 最大读写的块大小*/    size_t max_processing_chunk;    /* Backend-specific vars. 后端指定值*/    union {        /* In-memory buffer target. 目标内存buffer*/        struct {            sds ptr;            off_t pos;        } buffer;        /* Stdio file pointer target. stdio目标文件指针*/        struct {            FILE *fp;            off_t buffered; /* Bytes written since last fsync. 最后一次同步的位置*/            off_t autosync; /* fsync after 'autosync' bytes written. 在自动同步之后写入的位置*/        } file;        /* Multiple FDs target (used to write to N sockets). 多个fd目标*/        struct {            int *fds;       /* File descriptors. 文件描述符*/            int *state;     /* Error state of each fd. 0 (if ok) or errno. 状态*/            int numfds;            off_t pos;            sds buf;        } fdset;    } io;};typedef struct _rio rio;/* The following functions are our interface with the stream. They'll call the actual implementation of read / write / tell, and will update the checksum if needed. 以下功能是我们与流的接口,他们会调用read/write/tell的实际实现,并在需要时更新校验和*/static inline size_t rioWrite(rio *r, const void *buf, size_t len) {    while (len) {        size_t bytes_to_write = (r->max_processing_chunk && r->max_processing_chunk < len) ? r->max_processing_chunk : len;//判断buf是否大于最大块大小。        if (r->update_cksum) r->update_cksum(r,buf,bytes_to_write);//计算校验和        if (r->write(r,buf,bytes_to_write) == 0)//写入错误            return 0;        buf = (char*)buf + bytes_to_write;//更改指针位置        len -= bytes_to_write;//更改len        r->processed_bytes += bytes_to_write;//更改读写字节数    }    return 1;}static inline size_t rioRead(rio *r, void *buf, size_t len) {    while (len) {        size_t bytes_to_read = (r->max_processing_chunk && r->max_processing_chunk < len) ? r->max_processing_chunk : len;        if (r->read(r,buf,bytes_to_read) == 0)            return 0;        if (r->update_cksum) r->update_cksum(r,buf,bytes_to_read);        buf = (char*)buf + bytes_to_read;        len -= bytes_to_read;        r->processed_bytes += bytes_to_read;    }    return 1;}static inline off_t rioTell(rio *r) {    return r->tell(r);}static inline int rioFlush(rio *r) {    return r->flush(r);}//rio初始化void rioInitWithFile(rio *r, FILE *fp);void rioInitWithBuffer(rio *r, sds s);void rioInitWithFdset(rio *r, int *fds, int numfds);//释放文件集合void rioFreeFdset(rio *r);//rio写入块的操作size_t rioWriteBulkCount(rio *r, char prefix, int count);size_t rioWriteBulkString(rio *r, const char *buf, size_t len);size_t rioWriteBulkLongLong(rio *r, long long l);size_t rioWriteBulkDouble(rio *r, double d);//更新校验和void rioGenericUpdateChecksum(rio *r, const void *buf, size_t len);//自动同步void rioSetAutoSync(rio *r, off_t bytes);#endif

rdb.h

#ifndef __RDB_H#define __RDB_H#include <stdio.h>#include "rio.h"//读写文件的io操作方法/* TBD: include only necessary headers. 唯一必要的头文件*/#include "server.h"/* Test if a type is an object type. */#define rdbIsObjectType(t) ((t >= 0 && t <= 4) || (t >= 9 && t <= 14))int rdbSaveType(rio *rdb, unsigned char type);//保存类型int rdbLoadType(rio *rdb);//加载类型int rdbSaveTime(rio *rdb, time_t t);//保存时间time_t rdbLoadTime(rio *rdb);//加载时间int rdbSaveLen(rio *rdb, uint32_t len);//根据长度保存字符串对象uint32_t rdbLoadLen(rio *rdb, int *isencoded);//根据长度加载字符串对象int rdbSaveObjectType(rio *rdb, robj *o);//根据robj中的编码方式,保存到rdb文件中int rdbLoadObjectType(rio *rdb);//加载对象类型int rdbLoad(char *filename);//加载rdb文件int rdbSaveBackground(char *filename);//bgsaveint rdbSaveToSlavesSockets(void);//保存到从socketvoid rdbRemoveTempFile(pid_t childpid);//溢出临时文件int rdbSave(char *filename);//savessize_t rdbSaveObject(rio *rdb, robj *o);//保存robj到rdb中size_t rdbSavedObjectLen(robj *o);//获取保存后的robj长度robj *rdbLoadObject(int type, rio *rdb);//加载robj对象void backgroundSaveDoneHandler(int exitcode, int bysignal);//bgsave执行完成后的处理方法int rdbSaveKeyValuePair(rio *rdb, robj *key, robj *val, long long expiretime, long long now);//保存键值对robj *rdbLoadStringObject(rio *rdb);//加载字符串对象#endif

rdb.c

#include "server.h"#include "lzf.h"    /* LZF compression library */#include "zipmap.h"//压缩图的数据结构#include "endianconv.h"//大小端转换#include <math.h>#include <sys/types.h>#include <sys/time.h>#include <sys/resource.h>#include <sys/wait.h>#define rdbExitReportCorruptRDB(...) rdbCheckThenExit(__LINE__,__VA_ARGS__)//rdb检查后退出void rdbCheckThenExit(int linenum, char *reason, ...) {    va_list ap;    char msg[1024];    int len;    len = snprintf(msg,sizeof(msg),        "Internal error in RDB reading function at rdb.c:%d -> ", linenum);    va_start(ap,reason);//开始不定参数的读取    vsnprintf(msg+len,sizeof(msg)-len,reason,ap);    va_end(ap);    if (!rdbCheckMode) {        serverLog(LL_WARNING, "%s", msg);//写日志        char *argv[2] = {"",server.rdb_filename};        redis_check_rdb_main(2,argv);    } else {        rdbCheckError("%s",msg);    }    exit(1);}//写raw格式到文件static int rdbWriteRaw(rio *rdb, void *p, size_t len) {    if (rdb && rioWrite(rdb,p,len) == 0)        return -1;    return len;}//写类型到文件int rdbSaveType(rio *rdb, unsigned char type) {    return rdbWriteRaw(rdb,&type,1);}//加载类型int rdbLoadType(rio *rdb) {    unsigned char type;    if (rioRead(rdb,&type,1) == 0) return -1;    return type;}//加载时间time_t rdbLoadTime(rio *rdb) {    int32_t t32;    if (rioRead(rdb,&t32,4) == 0) return -1;    return (time_t)t32;}//保存时间int rdbSaveMillisecondTime(rio *rdb, long long t) {    int64_t t64 = (int64_t) t;    return rdbWriteRaw(rdb,&t64,8);}//加载时间long long rdbLoadMillisecondTime(rio *rdb) {    int64_t t64;    if (rioRead(rdb,&t64,8) == 0) return -1;    return (long long)t64;}//爆粗那边吗长度,第一个字节的前两位用来编码类型int rdbSaveLen(rio *rdb, uint32_t len) {    unsigned char buf[2];    size_t nwritten;    if (len < (1<<6)) {        /* Save a 6 bit len */        buf[0] = (len&0xFF)|(RDB_6BITLEN<<6);        if (rdbWriteRaw(rdb,buf,1) == -1) return -1;        nwritten = 1;    } else if (len < (1<<14)) {        /* Save a 14 bit len */        buf[0] = ((len>>8)&0xFF)|(RDB_14BITLEN<<6);        buf[1] = len&0xFF;        if (rdbWriteRaw(rdb,buf,2) == -1) return -1;        nwritten = 2;    } else {        /* Save a 32 bit len */        buf[0] = (RDB_32BITLEN<<6);        if (rdbWriteRaw(rdb,buf,1) == -1) return -1;        len = htonl(len);        if (rdbWriteRaw(rdb,&len,4) == -1) return -1;        nwritten = 1+4;    }    return nwritten;}//加载长度uint32_t rdbLoadLen(rio *rdb, int *isencoded) {    unsigned char buf[2];    uint32_t len;    int type;    if (isencoded) *isencoded = 0;    if (rioRead(rdb,buf,1) == 0) return RDB_LENERR;    type = (buf[0]&0xC0)>>6;    if (type == RDB_ENCVAL) {        /* Read a 6 bit encoding type. */        if (isencoded) *isencoded = 1;        return buf[0]&0x3F;    } else if (type == RDB_6BITLEN) {        /* Read a 6 bit len. */        return buf[0]&0x3F;    } else if (type == RDB_14BITLEN) {        /* Read a 14 bit len. */        if (rioRead(rdb,buf+1,1) == 0) return RDB_LENERR;        return ((buf[0]&0x3F)<<8)|buf[1];    } else if (type == RDB_32BITLEN) {        /* Read a 32 bit len. */        if (rioRead(rdb,&len,4) == 0) return RDB_LENERR;        return ntohl(len);    } else {        rdbExitReportCorruptRDB(            "Unknown length encoding %d in rdbLoadLen()",type);        return -1; /* Never reached. */    }}//编码整形,当它在编码类型的支持范围时,将value参数编码为整数,如果函数成功地对整数进行编码,则通过env将表示存储在缓冲区指针中,并返回字符串长度,否则返回0。int rdbEncodeInteger(long long value, unsigned char *enc) {    if (value >= -(1<<7) && value <= (1<<7)-1) {        enc[0] = (RDB_ENCVAL<<6)|RDB_ENC_INT8;        enc[1] = value&0xFF;        return 2;    } else if (value >= -(1<<15) && value <= (1<<15)-1) {        enc[0] = (RDB_ENCVAL<<6)|RDB_ENC_INT16;        enc[1] = value&0xFF;        enc[2] = (value>>8)&0xFF;        return 3;    } else if (value >= -((long long)1<<31) && value <= ((long long)1<<31)-1) {        enc[0] = (RDB_ENCVAL<<6)|RDB_ENC_INT32;        enc[1] = value&0xFF;        enc[2] = (value>>8)&0xFF;        enc[3] = (value>>16)&0xFF;        enc[4] = (value>>24)&0xFF;        return 5;    } else {        return 0;    }}//加载整数对象void *rdbLoadIntegerObject(rio *rdb, int enctype, int flags) {    int plain = flags & RDB_LOAD_PLAIN;    int encode = flags & RDB_LOAD_ENC;    unsigned char enc[4];    long long val;    if (enctype == RDB_ENC_INT8) {        if (rioRead(rdb,enc,1) == 0) return NULL;        val = (signed char)enc[0];    } else if (enctype == RDB_ENC_INT16) {        uint16_t v;        if (rioRead(rdb,enc,2) == 0) return NULL;        v = enc[0]|(enc[1]<<8);        val = (int16_t)v;    } else if (enctype == RDB_ENC_INT32) {        uint32_t v;        if (rioRead(rdb,enc,4) == 0) return NULL;        v = enc[0]|(enc[1]<<8)|(enc[2]<<16)|(enc[3]<<24);        val = (int32_t)v;    } else {        val = 0; /* anti-warning */        rdbExitReportCorruptRDB("Unknown RDB integer encoding type %d",enctype);    }    if (plain) {        char buf[LONG_STR_SIZE], *p;        int len = ll2string(buf,sizeof(buf),val);        p = zmalloc(len);        memcpy(p,buf,len);        return p;    } else if (encode) {        return createStringObjectFromLongLong(val);    } else {        return createObject(OBJ_STRING,sdsfromlonglong(val));    }}//如果字符串对象是数字,那么就会编码成8,16,32位的整数以节约空间int rdbTryIntegerEncoding(char *s, size_t len, unsigned char *enc) {    long long value;    char *endptr, buf[32];    /* Check if it's possible to encode this value as a number 检查是否可以转换为数字*/    value = strtoll(s, &endptr, 10);    if (endptr[0] != '\0') return 0;    ll2string(buf,32,value);    /* If the number converted back into a string is not identical then it's not possible to encode the string as integer 如果转换回字符串的数字不相同,则不可能将字符串编码为整数*/    if (strlen(buf) != len || memcmp(buf,s,len)) return 0;    return rdbEncodeInteger(value,enc);}//保存压缩后的string对象ssize_t rdbSaveLzfStringObject(rio *rdb, unsigned char *s, size_t len) {    size_t comprlen, outlen;    void *out;    /* We require at least four bytes compression for this to be worth it 我们要求至少4个字节才值得压缩*/    if (len <= 4) return 0;    outlen = len-4;    if ((out = zmalloc(outlen+1)) == NULL) return 0;    comprlen = lzf_compress(s, len, out, outlen);    if (comprlen == 0) {        zfree(out);        return 0;    }    ssize_t nwritten = rdbSaveLzfBlob(rdb, out, comprlen, len);    zfree(out);    return nwritten;}/* Load an LZF compressed string in RDB format. The returned value changes according to 'flags'. For more info check the rdbGenericLoadStringObject() function. 加载压缩的string对象*/void *rdbLoadLzfStringObject(rio *rdb, int flags) {    int plain = flags & RDB_LOAD_PLAIN;    unsigned int len, clen;    unsigned char *c = NULL;    sds val = NULL;    if ((clen = rdbLoadLen(rdb,NULL)) == RDB_LENERR) return NULL;    if ((len = rdbLoadLen(rdb,NULL)) == RDB_LENERR) return NULL;    if ((c = zmalloc(clen)) == NULL) goto err;    /* Allocate our target according to the uncompressed size. */    if (plain) {        val = zmalloc(len);    } else {        if ((val = sdsnewlen(NULL,len)) == NULL) goto err;    }    /* Load the compressed representation and uncompress it to target. */    if (rioRead(rdb,c,clen) == 0) goto err;    if (lzf_decompress(c,clen,val,len) == 0) {        if (rdbCheckMode) rdbCheckSetError("Invalid LZF compressed string");        goto err;    }    zfree(c);    if (plain)        return val;    else        return createObject(OBJ_STRING,val);err:    zfree(c);    if (plain)        zfree(val);    else        sdsfree(val);    return NULL;}//中间省略类似函数/* Load a string object from an RDB file according to flags: RDB_LOAD_NONE (no flags): load an RDB object, unencoded.根据标志从rdb文件加载string对象RDB_LOAD_ENC: If the returned type is a Redis object, try to              encode it in a special way to be more memory efficient. When this flag is passed the function no longer guarantees that obj->ptr is an SDS string.如果返回的类型是redis对象,尝试用特殊的方法进行编码以提高内存使用效率,当这个标志被传递时,函数不能保证obj->ptr是一个sds。 RDB_LOAD_PLAIN: Return a plain string allocated with zmalloc() instead of a Redis object with an sds in it.返回一个用zmalloc分配的纯字符串,而不是带有sds的redis对象 RDB_LOAD_SDS: Return an SDS string instead of a Redis object. */void *rdbGenericLoadStringObject(rio *rdb, int flags) {    int encode = flags & RDB_LOAD_ENC;    int plain = flags & RDB_LOAD_PLAIN;    int isencoded;    uint32_t len;    len = rdbLoadLen(rdb,&isencoded);    if (isencoded) {        switch(len) {        case RDB_ENC_INT8:        case RDB_ENC_INT16:        case RDB_ENC_INT32:            return rdbLoadIntegerObject(rdb,len,flags);        case RDB_ENC_LZF:            return rdbLoadLzfStringObject(rdb,flags);        default:            rdbExitReportCorruptRDB("Unknown RDB string encoding type %d",len);        }    }    if (len == RDB_LENERR) return NULL;    if (!plain) {        robj *o = encode ? createStringObject(NULL,len) :                           createRawStringObject(NULL,len);        if (len && rioRead(rdb,o->ptr,len) == 0) {            decrRefCount(o);            return NULL;        }        return o;    } else {        void *buf = zmalloc(len);        if (len && rioRead(rdb,buf,len) == 0) {            zfree(buf);            return NULL;        }        return buf;    }}//存储double到文件int rdbSaveDoubleValue(rio *rdb, double val) {    unsigned char buf[128];    int len;    if (isnan(val)) {        buf[0] = 253;        len = 1;    } else if (!isfinite(val)) {        len = 1;        buf[0] = (val < 0) ? 255 : 254;    } else {        if (DBL_MANT_DIG >= 52) && (LLONG_MAX == 0            snprintf((char*)buf+1,sizeof(buf)-1,"%.17g",val);        buf[0] = strlen((char*)buf+1);        len = buf[0]+1;    }    return rdbWriteRaw(rdb,buf,len);}/* For information about double serialization check rdbSaveDoubleValue() 加载double值*/int rdbLoadDoubleValue(rio *rdb, double *val) {    char buf[256];    unsigned char len;    if (rioRead(rdb,&len,1) == 0) return -1;    switch(len) {    case 255: *val = R_NegInf; return 0;    case 254: *val = R_PosInf; return 0;    case 253: *val = R_Nan; return 0;    default:        if (rioRead(rdb,buf,len) == 0) return -1;        buf[len] = '\0';        sscanf(buf, "%lg", val);        return 0;    }}

rdb.c中save和bgsave的实现,都会调用rdbsaverio来保存所有数据

int rdbSaveRio(rio *rdb, int *error) {    dictIterator *di = NULL;    dictEntry *de;    char magic[10];    int j;    long long now = mstime();    uint64_t cksum;    if (server.rdb_checksum)        rdb->update_cksum = rioGenericUpdateChecksum;//校验和    snprintf(magic,sizeof(magic),"REDIS%04d",RDB_VERSION);    if (rdbWriteRaw(rdb,magic,9) == -1) goto werr;//写入文件标志位的,用来标识redisrdb文件    if (rdbSaveInfoAuxFields(rdb) == -1) goto werr;//遍历所有数据库保存数据    for (j = 0; j < server.dbnum; j++) {        redisDb *db = server.db+j;//server中的数据库指针        dict *d = db->dict;//数据库的键空间        if (dictSize(d) == 0) continue;//如果没有数据就到下一个数据库        di = dictGetSafeIterator(d);//获取安全的字典迭代器        if (!di) return C_ERR;        /* Write the SELECT DB opcode 写入数据库的号码*/        if (rdbSaveType(rdb,RDB_OPCODE_SELECTDB) == -1) goto werr;        if (rdbSaveLen(rdb,j) == -1) goto werr;        /* Write the RESIZE DB opcode. We trim the size to UINT32_MAX, which is currently the largest type we are able to represent in RDB sizes. However this does not limit the actual size of the DB to load since these sizes are just hints to resize the hash tables. */        uint32_t db_size, expires_size;        db_size = (dictSize(db->dict) <= UINT32_MAX) ?                                dictSize(db->dict) :                                UINT32_MAX;        expires_size = (dictSize(db->expires) <= UINT32_MAX) ?                                dictSize(db->expires) :                                UINT32_MAX;        if (rdbSaveType(rdb,RDB_OPCODE_RESIZEDB) == -1) goto werr;        if (rdbSaveLen(rdb,db_size) == -1) goto werr;        if (rdbSaveLen(rdb,expires_size) == -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,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);    if (rioWrite(rdb,&cksum,8) == 0) goto werr;    return C_OK;werr:    if (error) *error = errno;    if (di) dictReleaseIterator(di);    return C_ERR;}/* Save the DB on disk. Return C_ERR on error, C_OK on success. 保存数据库到磁盘*/int rdbSave(char *filename) {    char tmpfile[256];    char cwd[MAXPATHLEN]; /* Current working dir path for error messages. 用来存储当前的工作路径*/    FILE *fp;    rio rdb;    int error = 0;    snprintf(tmpfile,256,"temp-%d.rdb", (int) getpid());    fp = fopen(tmpfile,"w");    if (!fp) {        char *cwdp = getcwd(cwd,MAXPATHLEN);        serverLog(LL_WARNING,            "Failed opening the RDB file %s (in server root dir %s) "            "for saving: %s",            filename,            cwdp ? cwdp : "unknown",            strerror(errno));        return C_ERR;    }    rioInitWithFile(&rdb,fp);//初始化rio结构体    if (rdbSaveRio(&rdb,&error) == C_ERR) {//将数据写入rdb文件的操作        errno = error;        goto werr;    }    /* Make sure data will not remain on the OS's output buffers 确定数据不会留在OS的输出缓冲区中*/    if (fflush(fp) == EOF) goto werr;    if (fsync(fileno(fp)) == -1) goto werr;    if (fclose(fp) == EOF) goto werr;    /* Use RENAME to make sure the DB file is changed atomically only if the generate DB file is ok. 如果db文件生成ok,使用重命名确定db文件改变是原子性的*/    if (rename(tmpfile,filename) == -1) {        char *cwdp = getcwd(cwd,MAXPATHLEN);        serverLog(LL_WARNING,            "Error moving temp DB file %s on the final "            "destination %s (in server root dir %s): %s",            tmpfile,            filename,            cwdp ? cwdp : "unknown",            strerror(errno));        unlink(tmpfile);        return C_ERR;    }    serverLog(LL_NOTICE,"DB saved on disk");    server.dirty = 0;    server.lastsave = time(NULL);    server.lastbgsave_status = C_OK;    return C_OK;werr:    serverLog(LL_WARNING,"Write error saving DB on disk: %s", strerror(errno));    fclose(fp);    unlink(tmpfile);    return C_ERR;}//bgsave的执行函数int rdbSaveBackground(char *filename) {    pid_t childpid;    long long start;    if (server.aof_child_pid != -1 || server.rdb_child_pid != -1) return C_ERR;    server.dirty_before_bgsave = server.dirty;    server.lastbgsave_try = time(NULL);    start = ustime();    if ((childpid = fork()) == 0) {        int retval;        /* Child pid=0的是子进程,执行save操作*/        closeListeningSockets(0);//子进程不需要执行客户端命令,关闭它的sockey        redisSetProcTitle("redis-rdb-bgsave");        retval = rdbSave(filename);//执行阻塞的保存函数        if (retval == C_OK) {            size_t private_dirty = zmalloc_get_private_dirty();//共享内存中写入数据            if (private_dirty) {                serverLog(LL_NOTICE,                    "RDB: %zu MB of memory used by copy-on-write",                    private_dirty/(1024*1024));            }        }        exitFromChild((retval == C_OK) ? 0 : 1);//退出子进程    } else {//服务进程的执行        /* Parent */        server.stat_fork_time = ustime()-start;        server.stat_fork_rate = (double) zmalloc_used_memory() * 1000000 / server.stat_fork_time / (1024*1024*1024); /* GB per second. */        latencyAddSampleIfNeeded("fork",server.stat_fork_time/1000);        if (childpid == -1) {//子进程出错,返回错误            server.lastbgsave_status = C_ERR;            serverLog(LL_WARNING,"Can't save in background: fork: %s",                strerror(errno));            return C_ERR;        }        serverLog(LL_NOTICE,"Background saving started by pid %d",childpid);        server.rdb_save_time_start = time(NULL);        server.rdb_child_pid = childpid;        server.rdb_child_type = RDB_CHILD_TYPE_DISK;        updateDictResizePolicy();//更新dict的rehash条件        return C_OK;    }    return C_OK; /* unreached */}//save命令void saveCommand(client *c) {    if (server.rdb_child_pid != -1) {        addReplyError(c,"Background save already in progress");        return;    }    if (rdbSave(server.rdb_filename) == C_OK) {        addReply(c,shared.ok);    } else {        addReply(c,shared.err);    }}/* BGSAVE [SCHEDULE] */void bgsaveCommand(client *c) {    int schedule = 0;    /* The SCHEDULE option changes the behavior of BGSAVE when an AOF rewrite is in progress. Instead of returning an error a BGSAVE gets scheduled. 当aof重写正在进行时,schedule会选则改变bgsave的行为,bgsave不会返回错误*/    if (c->argc > 1) {        if (c->argc == 2 && !strcasecmp(c->argv[1]->ptr,"schedule")) {            schedule = 1;        } else {            addReply(c,shared.syntaxerr);            return;        }    }    if (server.rdb_child_pid != -1) {        addReplyError(c,"Background save already in progress");    } else if (server.aof_child_pid != -1) {        if (schedule) {            server.rdb_bgsave_scheduled = 1;            addReplyStatus(c,"Background saving scheduled");        } else {            addReplyError(c,                "An AOF log rewriting in progress: can't BGSAVE right now. "                "Use BGSAVE SCHEDULE in order to schedule a BGSAVE whenver "                "possible.");        }    } else if (rdbSaveBackground(server.rdb_filename) == C_OK) {        addReplyStatus(c,"Background saving started");    } else {        addReply(c,shared.err);    }}

后面是一些主从复制使用socket通信的rdb实现,暂时不看。

原创粉丝点击