快乐的天空

时间来得快,去得也快

 

redis 持久化

redis有全量(save/bgsave)和增量(aof)的持久化命令。

全量的原理就是遍历里所有的DB,在每个bucket,读取链表的key和value并写入dump.rdb文件(rdb.c 405)。

save命令直接调度rdbSave函数,这会阻塞主线程的工作,通常我们使用bgsave。

bgsave命令调度rdbSaveBackground函数启动了一个子进程然后调度了rdbSave函数,子进程的退出状态由 serverCron的backgroundSaveDoneHandler来判断,这个在复制这篇文章里讲到过,这里就不罗嗦了。

除了直接的save、bgsave命令之外,还有几个地方还调用到rdbSaveBackground和rdbSave函数。

shutdown:redis关闭时需要对数据持久化,保证重启后数据一致性,会调用rdbSave()。

flushallCommand:清空redis数据后,如果不做立即执行一个rdbSave(),出现crash后,可能会载入含有老数据的dump.rdb。

void flushallCommand(redisClient *c) {   touchWatchedKeysOnFlush(-1);   server.dirty += emptyDb();      // 清空数据   addReply(c,shared.ok);   if (server.bgsavechildpid != -1) {     kill(server.bgsavechildpid,SIGKILL);     rdbRemoveTempFile(server.bgsavechildpid);   }   rdbSave(server.dbfilename);    //没有数据的dump.db   server.dirty++; }

sync:当master接收到slave发来的该命令的时候,会执行rdbSaveBackground,这个以前也有提过。

数据发生变化:在多少秒内出现了多少次变化则触发一次bgsave,这个可以在conf里配置

for (j = 0; j < server.saveparamslen; j++) {   struct saveparam *sp = server.saveparams+j;    if (server.dirty >= sp->changes && now-server.lastsave > sp->seconds) {      rdbSaveBackground(server.dbfilename);      break;   } }

增量备份就是aof,原理有点类似redo log。每次执行命令后如出现数据变化,会调用feedAppendOnlyFile,把数据写到server.aofbuf里。

void call(redisClient *c, struct redisCommand *cmd) {   long long dirty;   dirty = server.dirty;   cmd->proc(c);        //执行命令   dirty = server.dirty-dirty;   if (server.appendonly && dirty)     feedAppendOnlyFile(cmd,c->db->id,c->argv,c->argc);

待到下次循环的before_sleep函数会通过flushAppendOnlyFile函数把server.aofbuf里的数据write到append file里。
可以在redis.conf里配置每次write到append file后从page cache刷新到disk的规律。

# appendfsync always appendfsync everysec # appendfsync no

参数的原理MySQL的innodb_flush_log_at_trx_commit一样,是个比较影响io的一个参数,需要在高性能和不丢数据之间做tradeoff。软件的优化就是tradeoff的过程,没有银弹。

一个疑问先写到server.aofbuf,然后再写到数据文件,过程中如果crash会不会丢数据呢?

答案是不会,为何?我们来看函数执行的步骤:

call() feedAppendOnlyFile() 下一次循环 beforeSleep()-->flushAppendOnlyFile() aeMain()--->sendReplyToClient()

只有执行完了flush之后才会通知客户端数据写成功了,所以如果在feed和flush之间crash,客户接会因为进程退出接受到一个fin包,也就是一个错误的返回,所以数据并没有丢,只是执行出了错。

redis crash后,重启除了利用rdb重新载入数据外,还会读取append file(redis.c 1561)加载镜像之后的数据。

激活aof,可以在redis.conf配置文件里设置

appendonly yes

也可以通过config命令在运行态启动aof

cofig set appendonly yes

aof最大的问题就是随着时间append file会变的很大,所以我们需要bgrewriteaof命令重新整理文件,只保留最新的kv数据。

下面这个根据图形来描述一下aof的全过程,绿色的为aof.c里的函数,顺序从左到右。

启动appendly,或者config set appendonly yes 都会触发函数rewriteAppendOnlyFileBackground,bgrewriteaof也调用该函数。

实际最后调用的函数是rewriteAppendOnlyFile,这个函数与rdbSave和类似。保存全库的kv数据。

在子进程做快照的过程中,kv的变化是先写到aofbuf里。如果存在bgrewritechildpid进程,变化数据还要写到server.bgrewritebuf里(aof.c 177)。
等子进程完成快照退出之时,由backgroundRewriteDoneHandler函数再把bgrwritebuf和全镜像两部分数据进行合并(aof.c 673)。

合并后的aof文件才是最新的全库的镜像数据。

posted on 2012-08-01 17:30 探路者 阅读(504) 评论(0)  编辑 收藏 引用 所属分类: 学习笔记


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理


导航

统计

常用链接

留言簿

随笔分类

随笔档案

文章分类

文章档案

新闻档案

Android

Compiler Course

VIM

编译技术集合

测试

高性能计算

个人博客

框架/组件/库

搜索

最新评论

阅读排行榜

评论排行榜