Redis源码分析:snapshot

来源:互联网 发布:码字电脑 知乎 编辑:程序博客网 时间:2024/06/09 16:17
源码版本:redis 2.4.4

redis的snapshot通过将内存中的所有数据写入文件,以达到持久化的目的。
需要注意的是:
1)snapshot方式不是追加,而是将内存所有数据写入文件,snapshot间隔短的话,会造成磁盘IO频繁
2)在上一次做snapshot到当前,如果机器crash,期间修改过的数据会丢失

redis支持两种方式做snapshot
1)客户端发送bgsave命令(save命令也可以,其会阻塞主线程执行,不推荐)
2)根据配置的周期,定期执行

snapshot配置(redis.conf):
save A  B
在A秒后,如果至少B个数据发生变化则做snapshot。eg: save 3000100即在3000s后,如果至少100个key发生变化则做snapshot

rdbcompression yes
是否启用压缩(压缩消耗cpu)

dbfilename dump.rdb
snapshot dump的文件名

dir ./
指定工作目录,snapshot文件将保存在该目录,同时,aof文件也将保存在该目录

源码
执行bgsave时执行:

[cpp] view plain copy
  1. <pre name="code" class="cpp">void bgsaveCommand(redisClient *c) {  
  2.     if (server.bgsavechildpid != -1) { //无snapshot子进程运行  
  3.         addReplyError(c,"Background save already in progress");  
  4.     } else if (server.bgrewritechildpid != -1) { //无重写aof进程运行  
  5.         addReplyError(c,"Can't BGSAVE while AOF log rewriting is in progress");  
  6.     } else if (rdbSaveBackground(server.dbfilename) == REDIS_OK) { //执行snapshot  
  7.         addReplyStatus(c,"Background saving started");  
  8.     } else {  
  9.         addReply(c,shared.err);  
  10.     }  
  11. }</pre>  
  12. <pre></pre>  
  13. 检查是否满足snapshot条件:<br>  
  14. <pre name="code" class="cpp"><pre name="code" class="cpp">int serverCron(struct aeEventLoop *eventLoop, long long id, void *clientData)  
  15. {  
  16.         .....  
  17.         /* If there is not a background saving in progress check if 
  18.          * we have to save now */  
  19.          for (j = 0; j < server.saveparamslen; j++) {  
  20.             struct saveparam *sp = server.saveparams+j;  
  21.   
  22.             if (server.dirty >= sp->changes &&  
  23.                 now-server.lastsave > sp->seconds) {  
  24.                 redisLog(REDIS_NOTICE,"%d changes in %d seconds. Saving...",  
  25.                     sp->changes, sp->seconds);  
  26.                 rdbSaveBackground(server.dbfilename);  
  27.                 break;  
  28.             }  
  29.          }  
  30.         ......  
  31.  }</pre>  
  32. <pre></pre>  
  33. <p></p>  
  34. <p>执行snapshot:<br>  
  35. </p>  
  36. <pre name="code" class="cpp"><pre name="code" class="cpp">int rdbSaveBackground(char *filename) {  
  37.     pid_t childpid;  
  38.     long long start;  
  39.   
  40.     if (server.bgsavechildpid != -1) return REDIS_ERR;  
  41.     if (server.vm_enabled) waitEmptyIOJobsQueue();  
  42.     server.dirty_before_bgsave = server.dirty;  
  43.     start = ustime();  
  44.     if ((childpid = fork()) == 0) {  //fork子进程做snapshot  
  45.         /* Child */  
  46.         if (server.vm_enabled) vmReopenSwapFile();  
  47.         if (server.ipfd > 0) close(server.ipfd);  
  48.         if (server.sofd > 0) close(server.sofd);  
  49.         if (rdbSave(filename) == REDIS_OK) {   
  50.             _exit(0);  
  51.         } else {  
  52.             _exit(1);  
  53.         }  
  54.     } else {  
  55.         /* Parent */  
  56.         server.stat_fork_time = ustime()-start;  
  57.         if (childpid == -1) {  
  58.             redisLog(REDIS_WARNING,"Can't save in background: fork: %s",  
  59.                 strerror(errno));  
  60.             return REDIS_ERR;  
  61.         }  
  62.         redisLog(REDIS_NOTICE,"Background saving started by pid %d",childpid);  
  63.         server.bgsavechildpid = childpid;  
  64.         updateDictResizePolicy();  
  65.         return REDIS_OK;  
  66.     }  
  67.     return REDIS_OK; /* unreached */  
  68. }</pre>  
  69. <pre></pre>  
  70. 利用copy on write调用rdbsave将当前数据状态写入文件。<br>  
  71. <pre name="code" class="cpp">int rdbSave(char *filename) {  
  72.     dictIterator *di = NULL;  
  73.     dictEntry *de;  
  74.     FILE *fp;  
  75.     char tmpfile[256];  
  76.     int j;  
  77.     time_t now = time(NULL);  
  78.   
  79.     /* Wait for I/O therads to terminate, just in case this is a 
  80.      * foreground-saving, to avoid seeking the swap file descriptor at the 
  81.      * same time. */  
  82.     if (server.vm_enabled)  
  83.         waitEmptyIOJobsQueue();  
  84.   
  85.     snprintf(tmpfile,256,"temp-%d.rdb", (int) getpid());  
  86.     fp = fopen(tmpfile,"w");  
  87.     if (!fp) {  
  88.         redisLog(REDIS_WARNING, "Failed saving the DB: %s", strerror(errno));  
  89.         return REDIS_ERR;  
  90.     }  
  91.     if (fwrite("REDIS0002",9,1,fp) == 0) goto werr;  
  92.     for (j = 0; j < server.dbnum; j++) {  
  93.         redisDb *db = server.db+j;  
  94.         dict *d = db->dict;  
  95.         if (dictSize(d) == 0) continue;  
  96.         di = dictGetSafeIterator(d);  
  97.         if (!di) {  
  98.             fclose(fp);  
  99.             return REDIS_ERR;  
  100.         }  
  101.   
  102.         /* Write the SELECT DB opcode */  
  103.         if (rdbSaveType(fp,REDIS_SELECTDB) == -1) goto werr;  
  104.         if (rdbSaveLen(fp,j) == -1) goto werr;  
  105.   
  106.         /* Iterate this DB writing every entry */  
  107.         while((de = dictNext(di)) != NULL) {  
  108.             sds keystr = dictGetEntryKey(de);  
  109.             robj key, *o = dictGetEntryVal(de);  
  110.             time_t expiretime;  
  111.               
  112.             initStaticStringObject(key,keystr);  
  113.             expiretime = getExpire(db,&key);  
  114.   
  115.             /* Save the expire time */  
  116.             if (expiretime != -1) {  
  117.                 /* If this key is already expired skip it */  
  118.                 if (expiretime < now) continue;  
  119.                 if (rdbSaveType(fp,REDIS_EXPIRETIME) == -1) goto werr;  
  120.                 if (rdbSaveTime(fp,expiretime) == -1) goto werr;  
  121.             }  
  122.             /* Save the key and associated value. This requires special 
  123.              * handling if the value is swapped out. */  
  124.             if (!server.vm_enabled || o->storage == REDIS_VM_MEMORY ||  
  125.                                       o->storage == REDIS_VM_SWAPPING) {  
  126.                 int otype = getObjectSaveType(o);  
  127.   
  128.                 /* Save type, key, value */  
  129.                 if (rdbSaveType(fp,otype) == -1) goto werr;  
  130.                 if (rdbSaveStringObject(fp,&key) == -1) goto werr;  
  131.                 if (rdbSaveObject(fp,o) == -1) goto werr;  
  132.             } else {  
  133.                 /* REDIS_VM_SWAPPED or REDIS_VM_LOADING */  
  134.                 robj *po;  
  135.                 /* Get a preview of the object in memory */  
  136.                 po = vmPreviewObject(o);  
  137.                 /* Save type, key, value */  
  138.                 if (rdbSaveType(fp,getObjectSaveType(po)) == -1)  
  139.                     goto werr;  
  140.                 if (rdbSaveStringObject(fp,&key) == -1) goto werr;  
  141.                 if (rdbSaveObject(fp,po) == -1) goto werr;  
  142.                 /* Remove the loaded object from memory */  
  143.                 decrRefCount(po);  
  144.             }  
  145.         }  
  146.         dictReleaseIterator(di);  
  147.     }  
  148.     /* EOF opcode */  
  149.     if (rdbSaveType(fp,REDIS_EOF) == -1) goto werr;  
  150.   
  151.     /* Make sure data will not remain on the OS's output buffers */  
  152.     fflush(fp);  
  153.     fsync(fileno(fp));  
  154.     fclose(fp);  
  155.   
  156.     /* Use RENAME to make sure the DB file is changed atomically only 
  157.      * if the generate DB file is ok. */  
  158.     if (rename(tmpfile,filename) == -1) {  
  159.         redisLog(REDIS_WARNING,"Error moving temp DB file on the final destination: %s", strerror(errno));  
  160.         unlink(tmpfile);  
  161.         return REDIS_ERR;  
  162.     }  
  163.     redisLog(REDIS_NOTICE,"DB saved on disk");  
  164.     server.dirty = 0;  
  165.     server.lastsave = time(NULL);  
  166.     return REDIS_OK;  
  167.   
  168. werr:  
  169.     fclose(fp);  
  170.     unlink(tmpfile);  
  171.     redisLog(REDIS_WARNING,"Write error saving DB on disk: %s", strerror(errno));  
  172.     if (di) dictReleaseIterator(di);  
  173.     return REDIS_ERR;  
  174. }</pre>在serverCron  中检查做bgsave的子进程是否结束<pre name="code" class="cpp">   <pre name="code" class="cpp"/* Check if a background saving or AOF rewrite in progress terminated */  
  175.     if (server.bgsavechildpid != -1 || server.bgrewritechildpid != -1) { //是否有做snapshot或者aof rewrite的子进程存在  
  176.         int statloc;  
  177.         pid_t pid;  
  178.   
  179.         if ((pid = wait3(&statloc,WNOHANG,NULL)) != 0) {   
  180.             if (pid == server.bgsavechildpid) {  //结束了对其做后处理  
  181.                 backgroundSaveDoneHandler(statloc);  
  182.             } else {  
  183.                 backgroundRewriteDoneHandler(statloc);  
  184.             }  
  185.             updateDictResizePolicy();  
  186.         }  
  187.     } else {  
  188.          time_t now = time(NULL);</pre>  
  189. <pre></pre>  
  190. <br>  
  191. <pre name="code" class="cpp"><pre name="code" class="cpp">void backgroundSaveDoneHandler(int statloc) {  
  192.     int exitcode = WEXITSTATUS(statloc);  
  193.     int bysignal = WIFSIGNALED(statloc);  
  194.       
  195.     //判断做snapshot是否成功  
  196.     if (!bysignal && exitcode == 0) {  
  197.         redisLog(REDIS_NOTICE,  
  198.             "Background saving terminated with success");  
  199.         server.dirty = server.dirty - server.dirty_before_bgsave;  
  200.         server.lastsave = time(NULL);  
  201.     } else if (!bysignal && exitcode != 0) {  
  202.         redisLog(REDIS_WARNING, "Background saving error");  
  203.     } else {  
  204.         redisLog(REDIS_WARNING,  
  205.             "Background saving terminated by signal %d", WTERMSIG(statloc));  
  206.         rdbRemoveTempFile(server.bgsavechildpid);  
  207.     }  
  208.     server.bgsavechildpid = -1;  
  209.     /* Possibly there are slaves waiting for a BGSAVE in order to be served 
  210.      * (the first stage of SYNC is a bulk transfer of dump.rdb) */  
  211.     updateSlavesWaitingBgsave(exitcode == 0 ? REDIS_OK : REDIS_ERR);  
  212. }  
  213. </pre>  
  214. <pre></pre>  
  215. <br>  
  216. <br>  
  217. <br>  
  218. <p></p>  
  219.      
  220. </pre></pre></pre></pre>  
0 0