首页 > 代码库 > Redis数据持久化机制AOF原理分析二

Redis数据持久化机制AOF原理分析二

Redis数据持久化机制AOF原理分析二

分类: Redis 2014-01-12 15:36  737人阅读  评论(0)  收藏  举报
redis AOF rewrite

目录(?)[+]

本文所引用的源码全部来自Redis2.8.2版本。

Redis AOF数据持久化机制的实现相关代码是redis.c, redis.h, aof.c, bio.c, rio.c, config.c

在阅读本文之前请先阅读Redis数据持久化机制AOF原理分析之配置详解文章,了解AOF相关参数的解析,文章链接

http://blog.csdn.net/acceptedxukai/article/details/18135219

接着上一篇文章,本文将介绍Redis是如何实现AOF rewrite的。

转载请注明,文章出自http://blog.csdn.net/acceptedxukai/article/details/18181563


AOF rewrite的触发机制


如果Redis只是将客户端修改数据库的指令重现存储在AOF文件中,那么AOF文件的大小会不断的增加,因为AOF文件只是简单的重现存储了客户端的指令,而并没有进行合并。对于该问题最简单的处理方式,即当AOF文件满足一定条件时就对AOF进行rewrite,rewrite是根据当前内存数据库中的数据进行遍历写到一个临时的AOF文件,待写完后替换掉原来的AOF文件即可。


Redis触发AOF rewrite机制有三种:

1、Redis Server接收到客户端发送的BGREWRITEAOF指令请求,如果当前AOF/RDB数据持久化没有在执行,那么执行,反之,等当前AOF/RDB数据持久化结束后执行AOF rewrite

2、在Redis配置文件redis.conf中,用户设置了auto-aof-rewrite-percentage和auto-aof-rewrite-min-size参数,并且当前AOF文件大小server.aof_current_size大于auto-aof-rewrite-min-size(server.aof_rewrite_min_size),同时AOF文件大小的增长率大于auto-aof-rewrite-percentage(server.aof_rewrite_perc)时,会自动触发AOF rewrite

3、用户设置“config set appendonly yes”开启AOF的时,调用startAppendOnly函数会触发rewrite

下面分别介绍上述三种机制的处理.


接收到BGREWRITEAOF指令


[cpp]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. <span style="font-size:12px;">void bgrewriteaofCommand(redisClient *c) {  
  2.     //AOF rewrite正在执行,那么直接返回  
  3.     if (server.aof_child_pid != -1) {  
  4.         addReplyError(c,"Background append only file rewriting already in progress");  
  5.     } else if (server.rdb_child_pid != -1) {  
  6.         //AOF rewrite未执行,但RDB数据持久化正在执行,那么设置AOF rewrite状态为scheduled  
  7.         //待RDB结束后执行AOF rewrite  
  8.         server.aof_rewrite_scheduled = 1;  
  9.         addReplyStatus(c,"Background append only file rewriting scheduled");  
  10.     } else if (rewriteAppendOnlyFileBackground() == REDIS_OK) {  
  11.         //直接执行AOF rewrite  
  12.         addReplyStatus(c,"Background append only file rewriting started");  
  13.     } else {  
  14.         addReply(c,shared.err);  
  15.     }  
  16. }</span>  
当AOF rewrite请求被挂起时,在serverCron函数中,会处理。
[cpp]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. /* Start a scheduled AOF rewrite if this was requested by the user while 
  2.      * a BGSAVE was in progress. */  
  3.     // 如果用户执行 BGREWRITEAOF 命令的话,在后台开始 AOF 重写  
  4.     //当用户执行BGREWRITEAOF命令时,如果RDB文件正在写,那么将server.aof_rewrite_scheduled标记为1  
  5.     //当RDB文件写完后开启AOF rewrite  
  6.     if (server.rdb_child_pid == -1 && server.aof_child_pid == -1 &&  
  7.         server.aof_rewrite_scheduled)  
  8.     {  
  9.         rewriteAppendOnlyFileBackground();  
  10.     }  


Server自动对AOF进行rewrite

在serverCron函数中会周期性判断
[cpp]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. /* Trigger an AOF rewrite if needed */  
  2.          //满足一定条件rewrite AOF文件  
  3.          if (server.rdb_child_pid == -1 &&  
  4.              server.aof_child_pid == -1 &&  
  5.              server.aof_rewrite_perc &&  
  6.              server.aof_current_size > server.aof_rewrite_min_size)  
  7.          {  
  8.             long long base = server.aof_rewrite_base_size ?  
  9.                             server.aof_rewrite_base_size : 1;  
  10.             long long growth = (server.aof_current_size*100/base) - 100;  
  11.             if (growth >= server.aof_rewrite_perc) {  
  12.                 redisLog(REDIS_NOTICE,"Starting automatic rewriting of AOF on %lld%% growth",growth);  
  13.                 rewriteAppendOnlyFileBackground();  
  14.             }  
  15.          }  

config set appendonly yes

当客户端发送该指令时,config.c中的configSetCommand函数会做出响应,startAppendOnly函数会执行AOF rewrite
[cpp]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. if (!strcasecmp(c->argv[2]->ptr,"appendonly")) {  
  2.     int enable = yesnotoi(o->ptr);  
  3.   
  4.     if (enable == -1) goto badfmt;  
  5.     if (enable == 0 && server.aof_state != REDIS_AOF_OFF) {//appendonly no 关闭AOF  
  6.         stopAppendOnly();  
  7.     } else if (enable && server.aof_state == REDIS_AOF_OFF) {//appendonly yes rewrite AOF  
  8.         if (startAppendOnly() == REDIS_ERR) {  
  9.             addReplyError(c,  
  10.                 "Unable to turn on AOF. Check server logs.");  
  11.             return;  
  12.         }  
  13.     }  
  14. }  
[cpp]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. int startAppendOnly(void) {  
  2.     server.aof_last_fsync = server.unixtime;  
  3.     server.aof_fd = open(server.aof_filename,O_WRONLY|O_APPEND|O_CREAT,0644);  
  4.     redisAssert(server.aof_state == REDIS_AOF_OFF);  
  5.     if (server.aof_fd == -1) {  
  6.         redisLog(REDIS_WARNING,"Redis needs to enable the AOF but can‘t open the append only file: %s",strerror(errno));  
  7.         return REDIS_ERR;  
  8.     }  
  9.     if (rewriteAppendOnlyFileBackground() == REDIS_ERR) {//rewrite  
  10.         close(server.aof_fd);  
  11.         redisLog(REDIS_WARNING,"Redis needs to enable the AOF but can‘t trigger a background AOF rewrite operation. Check the above logs for more info about the error.");  
  12.         return REDIS_ERR;  
  13.     }  
  14.     /* We correctly switched on AOF, now wait for the rerwite to be complete 
  15.      * in order to append data on disk. */  
  16.     server.aof_state = REDIS_AOF_WAIT_REWRITE;  
  17.     return REDIS_OK;  
  18. }  

Redis AOF rewrite机制的实现

从上述分析可以看出rewrite的实现全部依靠rewriteAppendOnlyFileBackground函数,下面分析该函数,通过下面的代码可以看出,Redis是fork出一个子进程来操作AOF rewrite,然后子进程调用rewriteAppendOnlyFile函数,将数据写到一个临时文件temp-rewriteaof-bg-%d.aof中。如果子进程完成会通过exit(0)函数通知父进程rewrite结束,在serverCron函数中使用wait3函数接收子进程退出状态,然后执行后续的AOF rewrite的收尾工作,后面将会分析。
父进程的工作主要包括清楚server.aof_rewrite_scheduled标志,记录子进程IDserver.aof_child_pid = childpid,记录rewrite的开始时间server.aof_rewrite_time_start = time(NULL)等。
[cpp]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. int rewriteAppendOnlyFileBackground(void) {  
  2.     pid_t childpid;  
  3.     long long start;  
  4.   
  5.     // 后台重写正在执行  
  6.     if (server.aof_child_pid != -1) return REDIS_ERR;  
  7.     start = ustime();  
  8.     if ((childpid = fork()) == 0) {  
  9.         char tmpfile[256];  
  10.   
  11.         /* Child */  
  12.         closeListeningSockets(0);//  
  13.         redisSetProcTitle("redis-aof-rewrite");  
  14.         snprintf(tmpfile,256,"temp-rewriteaof-bg-%d.aof", (int) getpid());  
  15.         if (rewriteAppendOnlyFile(tmpfile) == REDIS_OK) {  
  16.             size_t private_dirty = zmalloc_get_private_dirty();  
  17.   
  18.             if (private_dirty) {  
  19.                 redisLog(REDIS_NOTICE,  
  20.                     "AOF rewrite: %zu MB of memory used by copy-on-write",  
  21.                     private_dirty/(1024*1024));  
  22.             }  
  23.             exitFromChild(0);  
  24.         } else {  
  25.             exitFromChild(1);  
  26.         }  
  27.     } else {  
  28.         /* Parent */  
  29.         server.stat_fork_time = ustime()-start;  
  30.         if (childpid == -1) {  
  31.             redisLog(REDIS_WARNING,  
  32.                 "Can‘t rewrite append only file in background: fork: %s",  
  33.                 strerror(errno));  
  34.             return REDIS_ERR;  
  35.         }  
  36.         redisLog(REDIS_NOTICE,  
  37.             "Background append only file rewriting started by pid %d",childpid);  
  38.         server.aof_rewrite_scheduled = 0;  
  39.         server.aof_rewrite_time_start = time(NULL);  
  40.         server.aof_child_pid = childpid;  
  41.         updateDictResizePolicy();  
  42.         /* We set appendseldb to -1 in order to force the next call to the 
  43.          * feedAppendOnlyFile() to issue a SELECT command, so the differences 
  44.          * accumulated by the parent into server.aof_rewrite_buf will start 
  45.          * with a SELECT statement and it will be safe to merge. */  
  46.         server.aof_selected_db = -1;  
  47.         replicationScriptCacheFlush();  
  48.         return REDIS_OK;  
  49.     }  
  50.     return REDIS_OK; /* unreached */  
  51. }  
接下来介绍rewriteAppendOnlyFile函数,该函数的主要工作为:遍历所有数据库中的数据,将其写入到临时文件temp-rewriteaof-%d.aof中,写入函数定义在rio.c中,比较简单,然后将数据刷新到硬盘中,然后将文件名rename为其调用者给定的临时文件名,注意仔细看代码,这里并没有修改为正式的AOF文件名。
在写入文件时如果设置server.aof_rewrite_incremental_fsync参数,那么在rioWrite函数中fwrite部分数据就会将数据fsync到硬盘中,来保证数据的正确性。
[cpp]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. int rewriteAppendOnlyFile(char *filename) {  
  2.     dictIterator *di = NULL;  
  3.     dictEntry *de;  
  4.     rio aof;  
  5.     FILE *fp;  
  6.     char tmpfile[256];  
  7.     int j;  
  8.     long long now = mstime();  
  9.   
  10.     /* Note that we have to use a different temp name here compared to the 
  11.      * one used by rewriteAppendOnlyFileBackground() function. */  
  12.     snprintf(tmpfile,256,"temp-rewriteaof-%d.aof", (int) getpid());  
  13.     fp = fopen(tmpfile,"w");  
  14.     if (!fp) {  
  15.         redisLog(REDIS_WARNING, "Opening the temp file for AOF rewrite in rewriteAppendOnlyFile(): %s", strerror(errno));  
  16.         return REDIS_ERR;  
  17.     }  
  18.   
  19.     rioInitWithFile(&aof,fp); //初始化读写函数,rio.c  
  20.     //设置r->io.file.autosync = bytes;每32M刷新一次  
  21.     if (server.aof_rewrite_incremental_fsync)  
  22.         rioSetAutoSync(&aof,REDIS_AOF_AUTOSYNC_BYTES);  
  23.     for (j = 0; j < server.dbnum; j++) {//遍历每个数据库  
  24.         char selectcmd[] = "*2\r\n$6\r\nSELECT\r\n";  
  25.         redisDb *db = server.db+j;  
  26.         dict *d = db->dict;  
  27.         if (dictSize(d) == 0) continue;  
  28.         di = dictGetSafeIterator(d);  
  29.         if (!di) {  
  30.             fclose(fp);  
  31.             return REDIS_ERR;  
  32.         }  
  33.   
  34.         /* SELECT the new DB */  
  35.         if (rioWrite(&aof,selectcmd,sizeof(selectcmd)-1) == 0) goto werr;  
  36.         if (rioWriteBulkLongLong(&aof,j) == 0) goto werr;  
  37.   
  38.         /* Iterate this DB writing every entry */  
  39.         while((de = dictNext(di)) != NULL) {  
  40.             sds keystr;  
  41.             robj key, *o;  
  42.             long long expiretime;  
  43.   
  44.             keystr = dictGetKey(de);  
  45.             o = dictGetVal(de);  
  46.             initStaticStringObject(key,keystr);  
  47.   
  48.             expiretime = getExpire(db,&key);  
  49.   
  50.             /* If this key is already expired skip it */  
  51.             if (expiretime != -1 && expiretime < now) continue;  
  52.   
  53.             /* Save the key and associated value */  
  54.             if (o->type == REDIS_STRING) {  
  55.                 /* Emit a SET command */  
  56.                 char cmd[]="*3\r\n$3\r\nSET\r\n";  
  57.                 if (rioWrite(&aof,cmd,sizeof(cmd)-1) == 0) goto werr;  
  58.                 /* Key and value */  
  59.                 if (rioWriteBulkObject(&aof,&key) == 0) goto werr;  
  60.                 if (rioWriteBulkObject(&aof,o) == 0) goto werr;  
  61.             } else if (o->type == REDIS_LIST) {  
  62.                 if (rewriteListObject(&aof,&key,o) == 0) goto werr;  
  63.             } else if (o->type == REDIS_SET) {  
  64.                 if (rewriteSetObject(&aof,&key,o) == 0) goto werr;  
  65.             } else if (o->type == REDIS_ZSET) {  
  66.                 if (rewriteSortedSetObject(&aof,&key,o) == 0) goto werr;  
  67.             } else if (o->type == REDIS_HASH) {  
  68.                 if (rewriteHashObject(&aof,&key,o) == 0) goto werr;  
  69.             } else {  
  70.                 redisPanic("Unknown object type");  
  71.             }  
  72.             /* Save the expire time */  
  73.             if (expiretime != -1) {  
  74.                 char cmd[]="*3\r\n$9\r\nPEXPIREAT\r\n";  
  75.                 if (rioWrite(&aof,cmd,sizeof(cmd)-1) == 0) goto werr;  
  76.                 if (rioWriteBulkObject(&aof,&key) == 0) goto werr;  
  77.                 if (rioWriteBulkLongLong(&aof,expiretime) == 0) goto werr;  
  78.             }  
  79.         }  
  80.         dictReleaseIterator(di);  
  81.     }  
  82.   
  83.     /* Make sure data will not remain on the OS‘s output buffers */  
  84.     fflush(fp);  
  85.     aof_fsync(fileno(fp));//将tempfile文件刷新到硬盘  
  86.     fclose(fp);  
  87.   
  88.     /* Use RENAME to make sure the DB file is changed atomically only 
  89.      * if the generate DB file is ok. */  
  90.     if (rename(tmpfile,filename) == -1) {//重命名文件名,注意rename后的文件也是一个临时文件  
  91.         redisLog(REDIS_WARNING,"Error moving temp append only file on the final destination: %s", strerror(errno));  
  92.         unlink(tmpfile);  
  93.         return REDIS_ERR;  
  94.     }  
  95.     redisLog(REDIS_NOTICE,"SYNC append only file rewrite performed");  
  96.     return REDIS_OK;  
  97.   
  98. werr:  
  99.     fclose(fp);  
  100.     unlink(tmpfile);  
  101.     redisLog(REDIS_WARNING,"Write error writing append only file on disk: %s", strerror(errno));  
  102.     if (di) dictReleaseIterator(di);  
  103.     return REDIS_ERR;  
  104. }  
AOF rewrite工作到这里已经结束一半,上一篇文章提到如果server.aof_state != REDIS_AOF_OFF,那么就会将客户端请求指令修改的数据通过feedAppendOnlyFile函数追加到AOF文件中,那么此时AOF已经rewrite了,必须要处理此时出现的差异数据,记得在feedAppendOnlyFile函数中有这么一段代码
[cpp]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. if (server.aof_child_pid != -1)  
  2.         aofRewriteBufferAppend((unsigned char*)buf,sdslen(buf));  
如果AOF rewrite正在进行,那么就将修改数据的指令字符串存储到server.aof_rewrite_buf_blocks链表中,等待AOF rewrite子进程结束后处理,处理此部分数据的代码在serverCron函数中。需要指出的是wait3函数我不了解,可能下面注释会有点问题。
[cpp]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. /* Check if a background saving or AOF rewrite in progress terminated. */  
  2. //如果RDB bgsave或AOF rewrite子进程已经执行,通过获取子进程的退出状态,对后续的工作进行处理  
  3. if (server.rdb_child_pid != -1 || server.aof_child_pid != -1) {//  
  4.     int statloc;  
  5.     pid_t pid;  
  6.   
  7.     if ((pid = wait3(&statloc,WNOHANG,NULL)) != 0) {  
  8.         int exitcode = WEXITSTATUS(statloc);//获取退出的状态  
  9.         int bysignal = 0;  
  10.   
  11.         if (WIFSIGNALED(statloc)) bysignal = WTERMSIG(statloc);  
  12.   
  13.         if (pid == server.rdb_child_pid) {  
  14.             backgroundSaveDoneHandler(exitcode,bysignal);  
  15.         } else if (pid == server.aof_child_pid) {  
  16.             backgroundRewriteDoneHandler(exitcode,bysignal);  
  17.         } else {  
  18.             redisLog(REDIS_WARNING,  
  19.                 "Warning, detected child with unmatched pid: %ld",  
  20.                 (long)pid);  
  21.         }  
  22.         // 如果 BGSAVE 和 BGREWRITEAOF 都已经完成,那么重新开始 REHASH  
  23.         updateDictResizePolicy();  
  24.     }  
  25. }  
对于AOF rewrite期间出现的差异数据,Server通过backgroundSaveDoneHandler函数将 server.aof_rewrite_buf_blocks链表中数据追加到新的AOF文件中。
backgroundSaveDoneHandler函数执行步骤:
1、通过判断子进程的退出状态,正确的退出状态为exit(0),即exitcode为0,bysignal我不清楚具体意义,如果退出状态正确,backgroundSaveDoneHandler函数才会开始处理
2、通过对rewriteAppendOnlyFileBackground函数的分析,可以知道rewrite后的AOF临时文件名为temp-rewriteaof-bg-%d.aof(%d=server.aof_child_pid)中,接着需要打开此临时文件
3、调用aofRewriteBufferWrite函数将server.aof_rewrite_buf_blocks中差异数据写到该临时文件中
4、如果旧的AOF文件未打开,那么打开旧的AOF文件,将文件描述符赋值给临时变量oldfd
5、将临时的AOF文件名rename为正常的AOF文件名
6、如果旧的AOF文件未打开,那么此时只需要关闭新的AOF文件,此时的server.aof_rewrite_buf_blocks数据应该为空;如果旧的AOF是打开的,那么将server.aof_fd指向newfd,然后根据相应的fsync策略将数据刷新到硬盘上
7、调用aofUpdateCurrentSize函数统计AOF文件的大小,更新server.aof_rewrite_base_size,为serverCron中自动AOF rewrite做相应判断
8、如果之前是REDIS_AOF_WAIT_REWRITE状态,则设置server.aof_state为REDIS_AOF_ON,因为只有“config set appendonly yes”指令才会设置这个状态,也就是需要写完快照后,立即打开AOF;而BGREWRITEAOF不需要打开AOF
9、调用后台线程去关闭旧的AOF文件
下面是backgroundSaveDoneHandler函数的注释代码

[cpp]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. /* A background append only file rewriting (BGREWRITEAOF) terminated its work. 
  2.  * Handle this. */  
  3. void backgroundRewriteDoneHandler(int exitcode, int bysignal) {  
  4.     if (!bysignal && exitcode == 0) {//子进程退出状态正确  
  5.         int newfd, oldfd;  
  6.         char tmpfile[256];  
  7.         long long now = ustime();  
  8.   
  9.         redisLog(REDIS_NOTICE,  
  10.             "Background AOF rewrite terminated with success");  
  11.   
  12.         /* Flush the differences accumulated by the parent to the 
  13.          * rewritten AOF. */  
  14.         snprintf(tmpfile,256,"temp-rewriteaof-bg-%d.aof",  
  15.             (int)server.aof_child_pid);  
  16.         newfd = open(tmpfile,O_WRONLY|O_APPEND);  
  17.         if (newfd == -1) {  
  18.             redisLog(REDIS_WARNING,  
  19.                 "Unable to open the temporary AOF produced by the child: %s", strerror(errno));  
  20.             goto cleanup;  
  21.         }  
  22.         //处理server.aof_rewrite_buf_blocks中DIFF数据  
  23.         if (aofRewriteBufferWrite(newfd) == -1) {  
  24.             redisLog(REDIS_WARNING,  
  25.                 "Error trying to flush the parent diff to the rewritten AOF: %s", strerror(errno));  
  26.             close(newfd);  
  27.             goto cleanup;  
  28.         }  
  29.   
  30.         redisLog(REDIS_NOTICE,  
  31.             "Parent diff successfully flushed to the rewritten AOF (%lu bytes)", aofRewriteBufferSize());  
  32.   
  33.         /* The only remaining thing to do is to rename the temporary file to 
  34.          * the configured file and switch the file descriptor used to do AOF 
  35.          * writes. We don‘t want close(2) or rename(2) calls to block the 
  36.          * server on old file deletion. 
  37.          * 
  38.          * There are two possible scenarios: 
  39.          * 
  40.          * 1) AOF is DISABLED and this was a one time rewrite. The temporary 
  41.          * file will be renamed to the configured file. When this file already 
  42.          * exists, it will be unlinked, which may block the server. 
  43.          * 
  44.          * 2) AOF is ENABLED and the rewritten AOF will immediately start 
  45.          * receiving writes. After the temporary file is renamed to the 
  46.          * configured file, the original AOF file descriptor will be closed. 
  47.          * Since this will be the last reference to that file, closing it 
  48.          * causes the underlying file to be unlinked, which may block the 
  49.          * server. 
  50.          * 
  51.          * To mitigate the blocking effect of the unlink operation (either 
  52.          * caused by rename(2) in scenario 1, or by close(2) in scenario 2), we 
  53.          * use a background thread to take care of this. First, we 
  54.          * make scenario 1 identical to scenario 2 by opening the target file 
  55.          * when it exists. The unlink operation after the rename(2) will then 
  56.          * be executed upon calling close(2) for its descriptor. Everything to 
  57.          * guarantee atomicity for this switch has already happened by then, so 
  58.          * we don‘t care what the outcome or duration of that close operation 
  59.          * is, as long as the file descriptor is released again. */  
  60.         if (server.aof_fd == -1) {  
  61.             /* AOF disabled */  
  62.   
  63.              /* Don‘t care if this fails: oldfd will be -1 and we handle that. 
  64.               * One notable case of -1 return is if the old file does 
  65.               * not exist. */  
  66.              oldfd = open(server.aof_filename,O_RDONLY|O_NONBLOCK);  
  67.         } else {  
  68.             /* AOF enabled */  
  69.             oldfd = -1; /* We‘ll set this to the current AOF filedes later. */  
  70.         }  
  71.   
  72.         /* Rename the temporary file. This will not unlink the target file if 
  73.          * it exists, because we reference it with "oldfd". */  
  74.         //把临时文件改名为正常的AOF文件名。由于当前oldfd已经指向这个之前的正常文件名的文件,  
  75.         //所以当前不会造成unlink操作,得等那个oldfd被close的时候,内核判断该文件没有指向了,就删除之。  
  76.         if (rename(tmpfile,server.aof_filename) == -1) {  
  77.             redisLog(REDIS_WARNING,  
  78.                 "Error trying to rename the temporary AOF file: %s", strerror(errno));  
  79.             close(newfd);  
  80.             if (oldfd != -1) close(oldfd);  
  81.             goto cleanup;  
  82.         }  
  83.         //如果AOF关闭了,那只要处理新文件,直接关闭这个新的文件即可  
  84.         //但是这里会不会导致服务器卡呢?这个newfd应该是临时文件的最后一个fd了,不会的,  
  85.         //因为这个文件在本函数不会写入数据,因为stopAppendOnly函数会清空aof_rewrite_buf_blocks列表。  
  86.         if (server.aof_fd == -1) {  
  87.             /* AOF disabled, we don‘t need to set the AOF file descriptor 
  88.              * to this new file, so we can close it. */  
  89.             close(newfd);  
  90.         } else {  
  91.             /* AOF enabled, replace the old fd with the new one. */  
  92.             oldfd = server.aof_fd;  
  93.             //指向新的fd,此时这个fd由于上面的rename语句存在,已经为正常aof文件名  
  94.             server.aof_fd = newfd;  
  95.             //fsync到硬盘  
  96.             if (server.aof_fsync == AOF_FSYNC_ALWAYS)  
  97.                 aof_fsync(newfd);  
  98.             else if (server.aof_fsync == AOF_FSYNC_EVERYSEC)  
  99.                 aof_background_fsync(newfd);  
  100.             server.aof_selected_db = -1; /* Make sure SELECT is re-issued */  
  101.             aofUpdateCurrentSize();  
  102.             server.aof_rewrite_base_size = server.aof_current_size;  
  103.   
  104.             /* Clear regular AOF buffer since its contents was just written to 
  105.              * the new AOF from the background rewrite buffer. */  
  106.             //rewrite得到的肯定是最新的数据,所以aof_buf中的数据没有意义,直接清空  
  107.             sdsfree(server.aof_buf);  
  108.             server.aof_buf = sdsempty();  
  109.         }  
  110.   
  111.         server.aof_lastbgrewrite_status = REDIS_OK;  
  112.   
  113.         redisLog(REDIS_NOTICE, "Background AOF rewrite finished successfully");  
  114.         /* Change state from WAIT_REWRITE to ON if needed */  
  115.         //下面判断是否需要打开AOF,比如bgrewriteaofCommand就不需要打开AOF。  
  116.         if (server.aof_state == REDIS_AOF_WAIT_REWRITE)  
  117.             server.aof_state = REDIS_AOF_ON;  
  118.   
  119.         /* Asynchronously close the overwritten AOF. */  
  120.         //让后台线程去关闭这个旧的AOF文件FD,只要CLOSE就行,会自动unlink的,因为上面已经有rename  
  121.         if (oldfd != -1) bioCreateBackgroundJob(REDIS_BIO_CLOSE_FILE,(void*)(long)oldfd,NULL,NULL);  
  122.   
  123.         redisLog(REDIS_VERBOSE,  
  124.             "Background AOF rewrite signal handler took %lldus", ustime()-now);  
  125.     } else if (!bysignal && exitcode != 0) {  
  126.         server.aof_lastbgrewrite_status = REDIS_ERR;  
  127.   
  128.         redisLog(REDIS_WARNING,  
  129.             "Background AOF rewrite terminated with error");  
  130.     } else {  
  131.         server.aof_lastbgrewrite_status = REDIS_ERR;  
  132.   
  133.         redisLog(REDIS_WARNING,  
  134.             "Background AOF rewrite terminated by signal %d", bysignal);  
  135.     }  
  136.   
  137. cleanup:  
  138.     aofRewriteBufferReset();  
  139.     aofRemoveTempFile(server.aof_child_pid);  
  140.     server.aof_child_pid = -1;  
  141.     server.aof_rewrite_time_last = time(NULL)-server.aof_rewrite_time_start;  
  142.     server.aof_rewrite_time_start = -1;  
  143.     /* Schedule a new rewrite if we are waiting for it to switch the AOF ON. */  
  144.     if (server.aof_state == REDIS_AOF_WAIT_REWRITE)  
  145.         server.aof_rewrite_scheduled = 1;  
  146. }  

至此,AOF数据持久化已经全部结束了,剩下的就是一些细节的处理,以及一些Linux库函数的理解,对于rename、unlink、wait3等库 函数的深入认识就去问Google吧。

小结


Redis AOF数据持久化的实现机制通过三篇文章基本上比较详细的分析了, 但这只是从代码层面去看AOF,对于AOF持久化的优缺点网上有很多分析,Redis的官方网站也有英文介绍,Redis的数据持久化还有一种方法叫RDB,更多RDB的内容等下次再分析。
感谢此篇博客给我在理解Redis AOF数据持久化方面的巨大帮助, http://chenzhenianqing.cn/articles/786.html,此篇博客对AOF的分析十分的详细。

Redis数据持久化机制AOF原理分析二