Redis中AOF與RDB持久化策略深入分析
寫(xiě)在前面
以下內(nèi)容是基于Redis 6.2.6 版本整理總結(jié)
一、Redis為什么要持久化
Redis 是一個(gè)內(nèi)存數(shù)據(jù)庫(kù),就是將數(shù)據(jù)庫(kù)中的內(nèi)容保存在內(nèi)存中,這與傳統(tǒng)的MySQL,Oracle等關(guān)系型數(shù)據(jù)庫(kù)直接將內(nèi)容保存到硬盤(pán)中相比,內(nèi)存數(shù)據(jù)庫(kù)的讀寫(xiě)效率比傳統(tǒng)數(shù)據(jù)庫(kù)要快的多(內(nèi)存的讀寫(xiě)效率遠(yuǎn)遠(yuǎn)大于硬盤(pán)的讀寫(xiě)效率)。但是內(nèi)存中存儲(chǔ)的缺點(diǎn)就是,一旦斷電或者宕機(jī),那么內(nèi)存數(shù)據(jù)庫(kù)中的數(shù)據(jù)將會(huì)全部丟失。而且,有時(shí)候redis需要重啟,要加載回原來(lái)的狀態(tài),也需要持久化重啟之前的狀態(tài)。
為了解決這個(gè)缺點(diǎn),Redis提供了將內(nèi)存數(shù)據(jù)持久化到硬盤(pán),以及用持久化文件來(lái)恢復(fù)數(shù)據(jù)庫(kù)數(shù)據(jù)的功能。Redis 支持兩種形式的持久化,一種是RDB快照(snapshotting),另外一種是AOF(append-only-file)。從Redis4.0版本開(kāi)始還通過(guò)RDB和AOF的混合持久化。
二、Redis的持久化方式
2.1. AOF持久化(Append of file)
OF采用的就是順序追加的方式,對(duì)于磁盤(pán)來(lái)說(shuō),順序?qū)懯亲羁?、最友好的方式。AOF文件存儲(chǔ)的是redis命令協(xié)議格式的數(shù)據(jù)。Redis通過(guò)重放AOF文件,也就是執(zhí)行AOF文件里的命令,來(lái)恢復(fù)數(shù)據(jù)。
2.1.1 fsync 系統(tǒng)調(diào)用
fsync 是系統(tǒng)調(diào)動(dòng)。內(nèi)核自己的機(jī)制,調(diào)用fysnc把數(shù)據(jù)從內(nèi)核緩沖區(qū)刷到磁盤(pán)。如果想主動(dòng)刷盤(pán),就write完調(diào)用一次fysnc。
2.1.2 AOF持久化策略
- always 在主線程中執(zhí)行,每次增刪改操作,都要調(diào)用fsync 落盤(pán),數(shù)據(jù)最安全,但效率最低
- every second 在后臺(tái)線程(bio_fsync_aof)中執(zhí)行,會(huì)丟1~2s的數(shù)據(jù)
- no 由操作系統(tǒng)決定什么時(shí)候刷盤(pán),不可控
缺點(diǎn):
對(duì)數(shù)據(jù)庫(kù)所有的修改命令(增刪改)都會(huì)記錄到AOF文件,數(shù)據(jù)冗余,隨著運(yùn)行時(shí)間增加,AOF文件會(huì)太過(guò)龐大,導(dǎo)致恢復(fù)速度變慢。比如:set key v1 ,set key v2 ,del key , set key v3,這四條命令都會(huì)被記錄。但最終的狀態(tài)就是key == v3,其余的命令就是冗余的數(shù)據(jù)。也就是說(shuō),我們只需要最后一個(gè)狀態(tài)即可。
2.1.3 aof_rewrite
redis針對(duì)AOF文件過(guò)大的問(wèn)題,推出了aof_rewrite來(lái)優(yōu)化。aof_rewrite 原理:通過(guò) fork 進(jìn)程,在子進(jìn)程中根據(jù)當(dāng)前內(nèi)存中的數(shù)據(jù)狀態(tài),生成命令協(xié)議數(shù)據(jù),也就是最新的狀態(tài)保存到aof文件,避免同一個(gè)key的歷史數(shù)據(jù)冗余,提升恢復(fù)速度。
在重寫(xiě)aof期間,redis的主進(jìn)程還在繼續(xù)響應(yīng)客戶(hù)端的請(qǐng)求,redis會(huì)將寫(xiě)請(qǐng)求寫(xiě)到重寫(xiě)的緩沖區(qū),等到子進(jìn)程aof持久化結(jié)束,給主進(jìn)程發(fā)信號(hào),主進(jìn)程再將重寫(xiě)緩沖區(qū)的數(shù)據(jù)追加到新的aof文件中。
雖然rewrite后AOF文件會(huì)變小,但aof還是要通過(guò)重放的方式恢復(fù)數(shù)據(jù),需要耗費(fèi)cpu資源,比較慢。
2.2 RDB快照(redis默認(rèn)持久化方式)
RDB是把當(dāng)前內(nèi)存中的數(shù)據(jù)集快照寫(xiě)入磁盤(pán)RDB文件,也就是 Snapshot 快照(數(shù)據(jù)庫(kù)中所有鍵值對(duì)二進(jìn)制數(shù)據(jù))?;謴?fù)時(shí)是將快照文件直接讀到內(nèi)存里。也是通過(guò)fork出子進(jìn)程去持久化。Redis沒(méi)有專(zhuān)門(mén)的載入RDB文件的命令,Redis服務(wù)器會(huì)在啟動(dòng)時(shí),如果檢測(cè)到了RDB文件就會(huì)自動(dòng)載入RDB文件。
觸發(fā)方式(自動(dòng)觸發(fā)和非自動(dòng)觸發(fā))
(1)自動(dòng)觸發(fā)
在redis.conf 文件中,SNAPSHOTTING 的配置選項(xiàng)就是用來(lái)配置自動(dòng)觸發(fā)條件。
save: 用來(lái)配置RDB持久化觸發(fā)的條件。save m n 表示 m 秒內(nèi),數(shù)據(jù)存在n次修改時(shí),自動(dòng)觸發(fā) bgsave (后臺(tái)持久化)。
save “” 表示禁用快照;
save 900 1:表示900 秒內(nèi)如果至少有 1 個(gè) key 的值變化,則保存;
save 300 10:表示300 秒內(nèi)如果至少有 10 個(gè) key 的值變化,則保存;
save 60 10000:表示60 秒內(nèi)如果至少有 10000 個(gè) key 的值變化,則保存。
如果你只需要使用Redis的緩存功能,不需要持久化,只需要注釋掉所有的save行即可。
stop-writes-on-bgsave-error: 默認(rèn)值為 yes。如果RDB快照開(kāi)啟,并且最近的一次快照保存失敗了,Redis會(huì)拒絕接收更新操作,以此來(lái)提醒用戶(hù)數(shù)據(jù)持久化失敗了,否則這些更新的數(shù)據(jù)可能會(huì)丟失。
rdbcompression:是否啟用RDB快照文件壓縮存儲(chǔ),默認(rèn)是開(kāi)啟的,當(dāng)數(shù)據(jù)量特別大時(shí),壓縮可以節(jié)省硬盤(pán)空間,但是會(huì)增加CPU消耗,可以選擇關(guān)閉來(lái)節(jié)省CPU資源,建議開(kāi)啟。
rdbchecksum:文件校驗(yàn),默認(rèn)開(kāi)啟。在Redis 5.0版本后,新增了校驗(yàn)功能,用于保證文件的完整性。開(kāi)啟這個(gè)選項(xiàng)會(huì)增加10%左右的性能損耗,如果追求高性能,可以關(guān)閉該選項(xiàng)。
dbfilename :RDB文件名,默認(rèn)為 dump.rdb
rdb-del-sync-files: Redis主從全量同步時(shí),通過(guò)RDB文件傳輸實(shí)現(xiàn)。如果沒(méi)有開(kāi)啟持久化,同步完成后,是否要移除主從同步的RDB文件,默認(rèn)為no。
dir:存放RDB和AOF持久化文件的目錄 默認(rèn)為當(dāng)前目錄
(2)手動(dòng)觸發(fā)
Redis手動(dòng)觸發(fā)RDB持久化的命令有兩種:
1)save :該命令會(huì)阻塞Redis主進(jìn)程,在save持久化期間,Redis不能響應(yīng)處理其他命令,這段時(shí)間Redis不可用,可能造成業(yè)務(wù)的停擺,直至RDB過(guò)程完成。一般不用。
2)bgsave:會(huì)在主進(jìn)程fork出子進(jìn)程進(jìn)行RDB的持久化。阻塞只發(fā)生在fork階段,而大key會(huì)導(dǎo)致fork時(shí)間增長(zhǎng)。
2.3 RDB和AOF混用
RDB借鑒了aof_rewrite的思路,就是rbd文件寫(xiě)完,再把重寫(xiě)緩沖區(qū)的數(shù)據(jù),追加到rbd文件的末尾,追加的這部分?jǐn)?shù)據(jù)的格式是AOF的命令格式,這就是rdb_aof的混用。
2.4 三種持久化方式比較
- AOF 優(yōu)點(diǎn):數(shù)據(jù)可靠,丟失少;缺點(diǎn):AOF 文件大,恢復(fù)速度慢;
- RDB 優(yōu)點(diǎn):RDB文件體積小,數(shù)據(jù)恢復(fù)快。缺點(diǎn):無(wú)法做到實(shí)時(shí)/秒級(jí)持久化,會(huì)丟失最后一次快照后的所有數(shù)據(jù)。每次bgsave運(yùn)行都需要fork進(jìn)程,主進(jìn)程和子進(jìn)程共享一份內(nèi)存空間,主進(jìn)程在繼續(xù)處理客戶(hù)端命令時(shí),采用的時(shí)寫(xiě)時(shí)復(fù)制技術(shù),只有修改的那部分內(nèi)存會(huì)重新復(fù)制出一份,更新頁(yè)表指向。復(fù)制出的那部分,會(huì)導(dǎo)致內(nèi)存膨脹。具體膨脹的程度,取決于主進(jìn)程修改的比例有多大。注意:子進(jìn)程只是讀取數(shù)據(jù),并不修改內(nèi)存中的數(shù)據(jù)。
三、什么是大key以及大key對(duì)持久化的影響
3.1 什么是大key
redis 是kv 中的v站用了大量的空間。比如當(dāng)v的類(lèi)型是hash、zset,并且里面存儲(chǔ)了大量的元素,這個(gè)v對(duì)應(yīng)的key就是大key。
3.2 fork進(jìn)程寫(xiě)時(shí)復(fù)制原理
在Redis主進(jìn)程中調(diào)用fork()函數(shù),創(chuàng)建出子進(jìn)程。這個(gè)子進(jìn)程在fork()函數(shù)返回時(shí),跟主進(jìn)程的狀態(tài)是一模一樣的。包括mm_struct和頁(yè)表。此時(shí),他們的頁(yè)表都被標(biāo)記為私有的寫(xiě)時(shí)復(fù)制狀態(tài)(只讀狀態(tài))。當(dāng)某個(gè)進(jìn)程試圖寫(xiě)某個(gè)數(shù)據(jù)頁(yè)時(shí),會(huì)觸發(fā)寫(xiě)保護(hù),內(nèi)核會(huì)重新為該進(jìn)程映射一段內(nèi)存,供其讀寫(xiě),并將頁(yè)表指向這個(gè)新的數(shù)據(jù)頁(yè)。
3.3 面試題-大key對(duì)持久化有什么影響
結(jié)合不同的持久化方式回答。fsync壓力大,fork時(shí)間長(zhǎng)。
如果是AOF:always、every second、no aof_rewrite
如果是RDB: rdb_aof
fork是在主進(jìn)程中執(zhí)行的,如果fork慢,會(huì)影響到主進(jìn)程的響應(yīng)。
四、持久化源碼分析
4.1 RDB持久化
4.1.1 RDB文件的創(chuàng)建
Redis是通過(guò)rdbSave函數(shù)來(lái)創(chuàng)建RDB文件的,SAVE 和 BGSAVE 會(huì)以不同的方式去調(diào)用rdbSave。
// src/rdb.c /* Save the DB on disk. Return C_ERR on error, C_OK on success. */ int rdbSave(char *filename, rdbSaveInfo *rsi) { char tmpfile[256]; char cwd[MAXPATHLEN]; /* Current working dir path for error messages. */ FILE *fp = NULL; rio rdb; int error = 0; snprintf(tmpfile,256,"temp-%d.rdb", (int) getpid()); fp = fopen(tmpfile,"w"); if (!fp) { char *cwdp = getcwd(cwd,MAXPATHLEN); serverLog(LL_WARNING, "Failed opening the RDB file %s (in server root dir %s) " "for saving: %s", filename, cwdp ? cwdp : "unknown", strerror(errno)); return C_ERR; } rioInitWithFile(&rdb,fp); startSaving(RDBFLAGS_NONE); if (server.rdb_save_incremental_fsync) rioSetAutoSync(&rdb,REDIS_AUTOSYNC_BYTES); if (rdbSaveRio(&rdb,&error,RDBFLAGS_NONE,rsi) == C_ERR) { errno = error; goto werr; } /* Make sure data will not remain on the OS's output buffers */ if (fflush(fp)) goto werr; if (fsync(fileno(fp))) goto werr; if (fclose(fp)) { fp = NULL; goto werr; } fp = NULL; /* Use RENAME to make sure the DB file is changed atomically only * if the generate DB file is ok. */ if (rename(tmpfile,filename) == -1) { char *cwdp = getcwd(cwd,MAXPATHLEN); serverLog(LL_WARNING, "Error moving temp DB file %s on the final " "destination %s (in server root dir %s): %s", tmpfile, filename, cwdp ? cwdp : "unknown", strerror(errno)); unlink(tmpfile); stopSaving(0); return C_ERR; } serverLog(LL_NOTICE,"DB saved on disk"); server.dirty = 0; server.lastsave = time(NULL); server.lastbgsave_status = C_OK; stopSaving(1); return C_OK; werr: serverLog(LL_WARNING,"Write error saving DB on disk: %s", strerror(errno)); if (fp) fclose(fp); unlink(tmpfile); stopSaving(0); return C_ERR; }
SAVE命令,在Redis主線程中執(zhí)行,如果save時(shí)間太長(zhǎng)會(huì)影響Redis的性能。
void saveCommand(client *c) { // 如果已經(jīng)有子進(jìn)程在進(jìn)行RDB持久化 if (server.child_type == CHILD_TYPE_RDB) { addReplyError(c,"Background save already in progress"); return; } rdbSaveInfo rsi, *rsiptr; rsiptr = rdbPopulateSaveInfo(&rsi); // 持久化 if (rdbSave(server.rdb_filename,rsiptr) == C_OK) { addReply(c,shared.ok); } else { addReplyErrorObject(c,shared.err); } }
BGSAVE命令是通過(guò)執(zhí)行rdbSaveBackground函數(shù),可以看到rdbSave的調(diào)用時(shí)在子進(jìn)程中。在BGSAVE執(zhí)行期間,客戶(hù)端發(fā)送的SAVE命令會(huì)被拒絕,禁止SAVE和BGSAVE同時(shí)執(zhí)行,主要時(shí)為了防止主進(jìn)程和子進(jìn)程同時(shí)執(zhí)行rdbSave,產(chǎn)生競(jìng)爭(zhēng);同理,也不能同時(shí)執(zhí)行兩個(gè)BGSAVE,也會(huì)產(chǎn)生競(jìng)爭(zhēng)條件。
/* BGSAVE [SCHEDULE] */ void bgsaveCommand(client *c) { int schedule = 0; /* The SCHEDULE option changes the behavior of BGSAVE when an AOF rewrite * is in progress. Instead of returning an error a BGSAVE gets scheduled. */ if (c->argc > 1) { if (c->argc == 2 && !strcasecmp(c->argv[1]->ptr,"schedule")) { schedule = 1; } else { addReplyErrorObject(c,shared.syntaxerr); return; } } rdbSaveInfo rsi, *rsiptr; rsiptr = rdbPopulateSaveInfo(&rsi); if (server.child_type == CHILD_TYPE_RDB) { addReplyError(c,"Background save already in progress"); } else if (hasActiveChildProcess()) { if (schedule) { server.rdb_bgsave_scheduled = 1; addReplyStatus(c,"Background saving scheduled"); } else { addReplyError(c, "Another child process is active (AOF?): can't BGSAVE right now. " "Use BGSAVE SCHEDULE in order to schedule a BGSAVE whenever " "possible."); } } else if (rdbSaveBackground(server.rdb_filename,rsiptr) == C_OK) { addReplyStatus(c,"Background saving started"); } else { addReplyErrorObject(c,shared.err); } } int rdbSaveBackground(char *filename, rdbSaveInfo *rsi) { pid_t childpid; if (hasActiveChildProcess()) return C_ERR; server.dirty_before_bgsave = server.dirty; server.lastbgsave_try = time(NULL); // 子進(jìn)程 if ((childpid = redisFork(CHILD_TYPE_RDB)) == 0) { int retval; /* Child */ redisSetProcTitle("redis-rdb-bgsave"); redisSetCpuAffinity(server.bgsave_cpulist); retval = rdbSave(filename,rsi); if (retval == C_OK) { sendChildCowInfo(CHILD_INFO_TYPE_RDB_COW_SIZE, "RDB"); } exitFromChild((retval == C_OK) ? 0 : 1); } else { /* Parent */ if (childpid == -1) { server.lastbgsave_status = C_ERR; serverLog(LL_WARNING,"Can't save in background: fork: %s", strerror(errno)); return C_ERR; } serverLog(LL_NOTICE,"Background saving started by pid %ld",(long) childpid); server.rdb_save_time_start = time(NULL); server.rdb_child_type = RDB_CHILD_TYPE_DISK; return C_OK; } return C_OK; /* unreached */ }
4.1.2 RDB文件的載入
Redis通過(guò)rdbLoad函數(shù)完成RDB文件的載入工作。Redis服務(wù)器在RDB的載入過(guò)程中會(huì)一直阻塞,直到完成加載。
int rdbLoad(char *filename, rdbSaveInfo *rsi, int rdbflags) { FILE *fp; rio rdb; int retval; if ((fp = fopen(filename,"r")) == NULL) return C_ERR; startLoadingFile(fp, filename,rdbflags); rioInitWithFile(&rdb,fp); retval = rdbLoadRio(&rdb,rdbflags,rsi); fclose(fp); stopLoading(retval==C_OK); return retval; }
4.2 AOF持久化
4.2.1 AOF持久化實(shí)現(xiàn)
- AOF命令追加:當(dāng)Redis服務(wù)器執(zhí)行完一個(gè)寫(xiě)命令后,會(huì)將該命令以協(xié)議格式追加到aof_buf緩沖區(qū)的末尾
- AOF文件的寫(xiě)入和同步:Redis服務(wù)是單線程的,主要在一個(gè)事件循環(huán)(event loop)中循環(huán)。Redis中事件分為文件事件和時(shí)間事件,文件事件負(fù)責(zé)接收客戶(hù)端的命令請(qǐng)求和給客戶(hù)端回復(fù)數(shù)據(jù),時(shí)間事件負(fù)責(zé)執(zhí)行定時(shí)任務(wù)。在一次的事件循環(huán)結(jié)束之前,都會(huì)調(diào)用flushAppendOnlyFile函數(shù),該函數(shù)會(huì)根據(jù)redis.conf配置文件中的持久化策略決定何時(shí)將aof_buf緩沖區(qū)中的命令數(shù)據(jù)寫(xiě)入的AOF文件。
4.2.2 源碼分析
// src/server.h /* Append only defines */ #define AOF_FSYNC_NO 0 #define AOF_FSYNC_ALWAYS 1 #define AOF_FSYNC_EVERYSEC 2 // src/aof.c void flushAppendOnlyFile(int force) { ssize_t nwritten; int sync_in_progress = 0; mstime_t latency; // 如果當(dāng)前aof_buf緩沖區(qū)為空 if (sdslen(server.aof_buf) == 0) { /* Check if we need to do fsync even the aof buffer is empty, * because previously in AOF_FSYNC_EVERYSEC mode, fsync is * called only when aof buffer is not empty, so if users * stop write commands before fsync called in one second, * the data in page cache cannot be flushed in time. */ if (server.aof_fsync == AOF_FSYNC_EVERYSEC && server.aof_fsync_offset != server.aof_current_size && server.unixtime > server.aof_last_fsync && !(sync_in_progress = aofFsyncInProgress())) { goto try_fsync; } else { return; } } if (server.aof_fsync == AOF_FSYNC_EVERYSEC) sync_in_progress = aofFsyncInProgress(); if (server.aof_fsync == AOF_FSYNC_EVERYSEC && !force) { /* With this append fsync policy we do background fsyncing. * If the fsync is still in progress we can try to delay * the write for a couple of seconds. */ if (sync_in_progress) { if (server.aof_flush_postponed_start == 0) { /* No previous write postponing, remember that we are * postponing the flush and return. */ server.aof_flush_postponed_start = server.unixtime; return; } else if (server.unixtime - server.aof_flush_postponed_start < 2) { /* We were already waiting for fsync to finish, but for less * than two seconds this is still ok. Postpone again. */ return; } /* Otherwise fall trough, and go write since we can't wait * over two seconds. */ server.aof_delayed_fsync++; serverLog(LL_NOTICE,"Asynchronous AOF fsync is taking too long (disk is busy?). Writing the AOF buffer without waiting for fsync to complete, this may slow down Redis."); } } /* We want to perform a single write. This should be guaranteed atomic * at least if the filesystem we are writing is a real physical one. * While this will save us against the server being killed I don't think * there is much to do about the whole server stopping for power problems * or alike */ if (server.aof_flush_sleep && sdslen(server.aof_buf)) { usleep(server.aof_flush_sleep); } latencyStartMonitor(latency); nwritten = aofWrite(server.aof_fd,server.aof_buf,sdslen(server.aof_buf)); latencyEndMonitor(latency); /* We want to capture different events for delayed writes: * when the delay happens with a pending fsync, or with a saving child * active, and when the above two conditions are missing. * We also use an additional event name to save all samples which is * useful for graphing / monitoring purposes. */ if (sync_in_progress) { latencyAddSampleIfNeeded("aof-write-pending-fsync",latency); } else if (hasActiveChildProcess()) { latencyAddSampleIfNeeded("aof-write-active-child",latency); } else { latencyAddSampleIfNeeded("aof-write-alone",latency); } latencyAddSampleIfNeeded("aof-write",latency); /* We performed the write so reset the postponed flush sentinel to zero. */ server.aof_flush_postponed_start = 0; if (nwritten != (ssize_t)sdslen(server.aof_buf)) { static time_t last_write_error_log = 0; int can_log = 0; /* Limit logging rate to 1 line per AOF_WRITE_LOG_ERROR_RATE seconds. */ if ((server.unixtime - last_write_error_log) > AOF_WRITE_LOG_ERROR_RATE) { can_log = 1; last_write_error_log = server.unixtime; } /* Log the AOF write error and record the error code. */ if (nwritten == -1) { if (can_log) { serverLog(LL_WARNING,"Error writing to the AOF file: %s", strerror(errno)); server.aof_last_write_errno = errno; } } else { if (can_log) { serverLog(LL_WARNING,"Short write while writing to " "the AOF file: (nwritten=%lld, " "expected=%lld)", (long long)nwritten, (long long)sdslen(server.aof_buf)); } if (ftruncate(server.aof_fd, server.aof_current_size) == -1) { if (can_log) { serverLog(LL_WARNING, "Could not remove short write " "from the append-only file. Redis may refuse " "to load the AOF the next time it starts. " "ftruncate: %s", strerror(errno)); } } else { /* If the ftruncate() succeeded we can set nwritten to * -1 since there is no longer partial data into the AOF. */ nwritten = -1; } server.aof_last_write_errno = ENOSPC; } /* Handle the AOF write error. */ if (server.aof_fsync == AOF_FSYNC_ALWAYS) { /* We can't recover when the fsync policy is ALWAYS since the reply * for the client is already in the output buffers (both writes and * reads), and the changes to the db can't be rolled back. Since we * have a contract with the user that on acknowledged or observed * writes are is synced on disk, we must exit. */ serverLog(LL_WARNING,"Can't recover from AOF write error when the AOF fsync policy is 'always'. Exiting..."); exit(1); } else { /* Recover from failed write leaving data into the buffer. However * set an error to stop accepting writes as long as the error * condition is not cleared. */ server.aof_last_write_status = C_ERR; /* Trim the sds buffer if there was a partial write, and there * was no way to undo it with ftruncate(2). */ if (nwritten > 0) { server.aof_current_size += nwritten; sdsrange(server.aof_buf,nwritten,-1); } return; /* We'll try again on the next call... */ } } else { /* Successful write(2). If AOF was in error state, restore the * OK state and log the event. */ if (server.aof_last_write_status == C_ERR) { serverLog(LL_WARNING, "AOF write error looks solved, Redis can write again."); server.aof_last_write_status = C_OK; } } server.aof_current_size += nwritten; /* Re-use AOF buffer when it is small enough. The maximum comes from the * arena size of 4k minus some overhead (but is otherwise arbitrary). */ if ((sdslen(server.aof_buf)+sdsavail(server.aof_buf)) < 4000) { sdsclear(server.aof_buf); } else { sdsfree(server.aof_buf); server.aof_buf = sdsempty(); } try_fsync: /* Don't fsync if no-appendfsync-on-rewrite is set to yes and there are * children doing I/O in the background. */ if (server.aof_no_fsync_on_rewrite && hasActiveChildProcess()) return; /* Perform the fsync if needed. */ if (server.aof_fsync == AOF_FSYNC_ALWAYS) { /* redis_fsync is defined as fdatasync() for Linux in order to avoid * flushing metadata. */ latencyStartMonitor(latency); /* Let's try to get this data on the disk. To guarantee data safe when * the AOF fsync policy is 'always', we should exit if failed to fsync * AOF (see comment next to the exit(1) after write error above). */ if (redis_fsync(server.aof_fd) == -1) { serverLog(LL_WARNING,"Can't persist AOF for fsync error when the " "AOF fsync policy is 'always': %s. Exiting...", strerror(errno)); exit(1); } latencyEndMonitor(latency); latencyAddSampleIfNeeded("aof-fsync-always",latency); server.aof_fsync_offset = server.aof_current_size; server.aof_last_fsync = server.unixtime; } else if ((server.aof_fsync == AOF_FSYNC_EVERYSEC && server.unixtime > server.aof_last_fsync)) { if (!sync_in_progress) { aof_background_fsync(server.aof_fd); server.aof_fsync_offset = server.aof_current_size; } server.aof_last_fsync = server.unixtime; } }
到此這篇關(guān)于Redis中AOF與RDB持久化策略深入分析的文章就介紹到這了,更多相關(guān)Redis持久化策略?xún)?nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Redis模糊查詢(xún)的幾種實(shí)現(xiàn)方法
本文主要介紹了Redis模糊查詢(xún)的幾種實(shí)現(xiàn)方法,包括兩種方法KEYS , SCAN,具有一定的參考價(jià)值,感興趣的可以了解一下2024-02-02Redis序列化轉(zhuǎn)換類(lèi)型報(bào)錯(cuò)的解決
本文主要介紹了Redis序列化轉(zhuǎn)換類(lèi)型報(bào)錯(cuò)的解決,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-04-04redis.clients.jedis.exceptions.JedisDataException異常的錯(cuò)誤解決
本文主要介紹了redis.clients.jedis.exceptions.JedisDataException異常的錯(cuò)誤解決,這個(gè)異常通常發(fā)生在嘗試連接到一個(gè)?Redis?服務(wù)器時(shí),客戶(hù)端發(fā)送了一個(gè)?AUTH?命令來(lái)驗(yàn)證密碼,但是沒(méi)有配置密碼驗(yàn)證,下來(lái)就來(lái)解決一下2024-05-05從原理到實(shí)踐分析?Redis?分布式鎖的多種實(shí)現(xiàn)方案
在分布式系統(tǒng)中,為了保證多個(gè)進(jìn)程或線程之間的數(shù)據(jù)一致性和正確性,需要使用鎖來(lái)實(shí)現(xiàn)互斥訪問(wèn)共享資源,然而,使用本地鎖在分布式系統(tǒng)中存在問(wèn)題,這篇文章主要介紹了從原理到實(shí)踐分析?Redis?分布式鎖的多種實(shí)現(xiàn)方案,需要的朋友可以參考下2024-07-07redis底層數(shù)據(jù)結(jié)構(gòu)之ziplist實(shí)現(xiàn)詳解
這篇文章主要為大家介紹了redis底層數(shù)據(jù)結(jié)構(gòu)之ziplist實(shí)現(xiàn)詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-12-12Redis數(shù)據(jù)庫(kù)中實(shí)現(xiàn)分布式鎖的方法
這篇文章主要介紹了Redis數(shù)據(jù)庫(kù)中實(shí)現(xiàn)分布式鎖的方法,Redis是一個(gè)高性能的主存式數(shù)據(jù)庫(kù),需要的朋友可以參考下2015-06-06