(一)分析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持久化过程
触发rdb同步写入快照文件的形式有三种:
1)自动触发:save,bgsave命令
2)根据redis.conf,serverCron触发
3)主从同步时触发;
lldb跟踪效果
生成临时文件
临时文件替换过程
临时文件rename后变为dump.rdb文件,则文件的md5会发生改变。
rdb.c 源码:
/* SAVE 命令*/
voidsaveCommand(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 命令*/
voidbgsaveCommand(client *c) {
int schedule = 0;
...省略
if (server.rdb_child_pid != -1) { 。//判断是否有RDB子进程
addReplyError(c,"Background save already in progress");
} elseif (server.aof_child_pid != -1) { //判断是否有AOF子进程
...省略
} elseif (rdbSaveBackground(server.rdb_filename,NULL) == C_OK) { //异步创建快照
addReplyStatus(c,"Background saving started");
} else {
addReply(c,shared.err);
}
}
intrdbSave(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;
}
intrdbSaveBackground(char *filename, rdbSaveInfo *rsi) {
pid_t childpid;
longlong 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了子进程去完成写入过程;
aof文件
总结:
rdb持久化:
1)rdb文件比较适合用于数据恢复,但是它不安全。容易造成数据丢失;
2)rdb文件数据大的情况下写入时会有一些影响;
3) 如果
aof持久化:
1)aof在数据安全性上会比rdb高,如果突发意外情况,只会丢失1秒数据;
2)aof生成的文件比较大,如果使用incr增加到100,则会有100条记录;