【Redis源码】详细的RDB和AOF持久化过程(五)

(一)分析RDB和AOF的优劣势:

 

优劣势官方介绍:https://redis.io/topics/persistence

持久化详细介绍:http://oldblog.antirez.com/post/redis-persistence-demystified.html

 

1.RDB:

RDB的优点:

1)RDB是redis数据非常紧凑的单文件时间点表示。RDB文件非常适合备份。例如,您可能希望在最近的24小时内每小时存档一次

RDB文件,并在30天之内每天保存一次RDB快照。这使您可以在灾难情况下轻松还原数据集的不同版本。

2) RDB对于灾难恢复非常有用,它是一个紧凑文件,可以传输到远程中心或Amazon S上。

3)RDB最大限度地提高了redis的性能,因为redis父进程为了持久化而需要做的唯一个就是开辟一个子进程,其余所有工作都要做。父进程将永远不会执行磁盘I/O;

4)与AOF相比,RDB允许大型数据集更快地重启。

RDB的缺点:

1)如果需要最大程度地减少数据丢失的可能性(比如说redis停止工作,例如说断电之后)。则RDB不好。您可以在生成RDB的位置配置不同的保存点(例如,在至少5分钟之后,对数据集进行100次写入,但是您可以有多个保存点)。但是,通常会每个5分钟或更长的时间创建一次RDB的快照,因此,如果Redis出于任何原因在没有正确关闭的情况下停止工作,则应该准备丢失最新的数据分钟。

2)RDB需要经常使用fork()函数才能使用子进程将其持久化在磁盘上。如果数据集很大,fork()可能很耗时,并且如果数据集很大且

CPU性能不佳,则可能导致Redis停止为服务器服务几毫秒甚至一秒种。AOF也是用fork(),但是您可以调整要重写日志的频率,而无需权衡持久性。

 

2.AOF

AOF的优点:

1)使用AOF Redis更加持久:您可以使用不同的fsync策略:完全没有fsync,每秒fsync,每个查询fsync。使用默认策略fsync时,每秒的写入性能仍然很好(fsync是使用后台线程执行的,并且在没有进行fsync的情况下,主线程将尽力执行写入操作。)但是您只能损失一秒钟的写入时间。

2)AOF日志是仅追加的日志,因此,如果断电,则不会出现寻道或损坏问题。即使由于某种原因(磁盘已满或其他原因)以半写命令结束日志,redis-check-aof工具也可以轻松修复它。

3)Redis太大时,Redis可以在后台自动重写AOF。重写是完全安全的,因为Redis继续追加到旧文件时,会生成一个全新的文件,其中包含创建当前数据集所需的最少操作集,一旦准备好第二个文件,Redis会切换这两个文件并开始追加到新的那一个。

4)AOF以易于理解和解析的格式包含所有操作的日志。您甚至可以轻松导出AOF文件。例如,即使您使用FLUSHALL命令刷新了所有错误文件,如果在此期间未执行任何日志重写操作,您仍然可以保存数据集,只是停止服务器,删除最新命令并重新启动Redis。

 

AOF的缺点:

1)对于同一数据集,AOF文件通常大于等效的RDB文件。

2)根据确切的fsync策略,AOF可能比RDB慢。通常,在将fsync设置为每秒的情况下,性能仍然很高,并且在禁用fsync的情况下,即使在高负载下,它也应与RDB一样快。即使在巨大的写负载的情况下,RDB仍然能够提供有关最大延迟的更多保证。

3)过去,我们在特定命令中遇到过罕见的错误(例如,其中有一个涉及阻止命令,例如BPROPLPUSH),导致生成的AOF在重载时无法重现完全相同的数据集。这些错误很少见,我们在测试套件中进行了测试,自动创建了随机的复杂数据集,然后重新加载它们以检查一切是否正常。但是,RDB持久性几乎是不可能的。为了更清楚地说明这一点:Redis AOF通过增量更新现有状态来工作,就像MySQL或MongoDB一样,而RDB快照一次又一次地创建所有内容,从概念上讲更健壮。但是- 1)应该注意的是,每次Redis重写AOF时,都会从数据集中包含的实际数据开始重新创建AOF,与始终附加AOF文件(或重写为读取旧AOF而不是读取内存中的数据)相比,提高了对错误的抵抗力。2)我们从未收到过有关真实环境中检测到的AOF损坏的用户报告。

 

(二)redis.conf配置

dbfilename dump.rdb              #rdb持久化文件名称

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

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

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

appendonly yes                   #是否开启aof。no关闭,yes开启
appendfilename "appendonly.aof"  #aof文件名称

 
appendfsync everysec   #设置项可以设置  everysec、always、no 三种        
#everysec fsync每秒写入。always fsync每次每次将新命令附加AOF文件,no 从不fsync,只需将数据交给操作系统即可。
#no是最快的一种,但是不安全。通常,Linux使用此配置每30秒刷新一次数据。但这取决于内核的精准调整。

aof-use-rdb-preamble yes #混合模式

(三)RDB持久化过程

【Redis源码】详细的RDB和AOF持久化过程(五)_第1张图片

触发rdb同步写入快照文件的形式有三种:

1)自动触发: save,bgsave命令

2)根据redis.conf,serverCron触发

3)主从同步时触发;

 

lldb跟踪效果

【Redis源码】详细的RDB和AOF持久化过程(五)_第2张图片

生成临时文件

【Redis源码】详细的RDB和AOF持久化过程(五)_第3张图片

临时文件替换过程

【Redis源码】详细的RDB和AOF持久化过程(五)_第4张图片

临时文件rename后变为dump.rdb文件,则文件的md5会发生改变。 

rdb.c 源码:

 

/* SAVE 命令*/
void saveCommand(client *c) {
    if (server.rdb_child_pid != -1) {  //判断是否RDB有子进程
        addReplyError(c,"Background save already in progress");
        return;
    }
    if (rdbSave(server.rdb_filename,NULL) == C_OK) {
        addReply(c,shared.ok);
    } else {
        addReply(c,shared.err);
    }
}

/* BGSAVE 命令*/
void bgsaveCommand(client *c) {
    int schedule = 0;
    ...省略
    
    if (server.rdb_child_pid != -1) { 。   //判断是否有RDB子进程
        addReplyError(c,"Background save already in progress");
    } else if (server.aof_child_pid != -1) { //判断是否有AOF子进程
        ...省略
    } else if (rdbSaveBackground(server.rdb_filename,NULL) == C_OK) { //异步创建快照
        addReplyStatus(c,"Background saving started");
    } else {
        addReply(c,shared.err);
    }
}

int rdbSave(char *filename, rdbSaveInfo *rsi) {
    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 (rdbSaveRio(&rdb,&error,RDB_SAVE_NONE,rsi) == C_ERR) { //写入数据
        errno = error;
        goto werr;
    }

    /* Make sure data will not remain on the OS's output buffers */
    if (fflush(fp) == EOF) goto werr;
    if (fsync(fileno(fp)) == -1) goto werr;
    if (fclose(fp) == EOF) goto werr;
 
    //临时文件替换成redis.conf的dbfilename的配置快照名称
    if (rename(tmpfile,filename) == -1) {
      ...省略
    }
    return C_OK;

werr:
    serverLog(LL_WARNING,"Write error saving DB on disk: %s", strerror(errno));
    fclose(fp);
    unlink(tmpfile);
    return C_ERR;
}

int rdbSaveBackground(char *filename, rdbSaveInfo *rsi) {
    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);
    openChildInfoPipe();

    start = ustime();
    if ((childpid = fork()) == 0) { //fork子进程
        int retval;

        /* Child */
        closeListeningSockets(0);
        redisSetProcTitle("redis-rdb-bgsave");
        retval = rdbSave(filename,rsi);  //调用rdbSave生成快照
        ...省略
        exitFromChild((retval == C_OK) ? 0 : 1);
    } else {
        /* 父进程 */
        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);
        ...省略
        return C_OK;
    }
    return C_OK; /* unreached */
}

(四)AOF持久化过程

AOF写入形式有有两种:

1)bgrewriteaof命令写入

2)serverCron中调中写入

aof与rdb不同的是,aof是完全异步写入。fork了子进程去完成写入过程;

 【Redis源码】详细的RDB和AOF持久化过程(五)_第5张图片

aof文件

【Redis源码】详细的RDB和AOF持久化过程(五)_第6张图片

aof存储的其实就是每次操作命令

 

总结:

 

rdb持久化:

1)rdb文件比较适合用于数据恢复,但是它不安全。容易造成数据丢失;

2)rdb文件数据大的情况下写入时会有一些影响;

3) 如果

 

aof持久化:

1)aof在数据安全性上会比rdb高,如果突发意外情况,只会丢失1秒数据;

2)aof生成的文件比较大,如果使用incr增加到100,则会有100条记录;

 

 

 

 

 

你可能感兴趣的:(Redis,C++,AOF,RDB,RDB和AOF,Redis,源码)