技术头条 - 一个快速在微博传播文章的方式     搜索本站
您现在的位置首页 --> 源码分析 --> redis源代码分析 - persistence

redis源代码分析 - persistence

浏览:31117次  出处信息

    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文件才是最新的全库的镜像数据。

    ps:在最新的2.2.11版本的stopAppendOnly函数里存在一个bug,应该杀的进程是bgrewriteachilidpid进程,bgsavechildpid不不幸中枪,和Salvatore Sanfilippo邮件交流了一下,他承认了这个bug。 说2.2版本忘记改了。

建议继续学习:

  1. Redis消息队列的若干实现方式    (阅读:10686)
  2. 基于Redis构建系统的经验和教训    (阅读:9256)
  3. 浅谈redis数据库的键值设计    (阅读:8266)
  4. redis在大数据量下的压测表现    (阅读:7363)
  5. redis运维的一些知识点    (阅读:7391)
  6. Redis和Memcached的区别    (阅读:6741)
  7. Redis作者谈Redis应用场景    (阅读:6545)
  8. redis 运维实际经验纪录之一    (阅读:6451)
  9. 记Redis那坑人的HGETALL    (阅读:6301)
  10. Redis内存存储结构分析    (阅读:6168)
QQ技术交流群:445447336,欢迎加入!
扫一扫订阅我的微信号:IT技术博客大学习
© 2009 - 2024 by blogread.cn 微博:@IT技术博客大学习

京ICP备15002552号-1