成人怡红院-成人怡红院视频在线观看-成人影视大全-成人影院203nnxyz-美女毛片在线看-美女免费黄

站長資訊網(wǎng)
最全最豐富的資訊網(wǎng)站

Redis經(jīng)典技巧之詳解持久化原理

本篇文章給大家?guī)砹岁P(guān)于Redis的相關(guān)知識(shí),其中主要介紹了持久化的相關(guān)問題,可以從幾個(gè)方面介紹一下redis持久化的機(jī)制原理,希望對(duì)大家有幫助。

Redis經(jīng)典技巧之詳解持久化原理

推薦學(xué)習(xí):Redis教程

本文將從以下幾個(gè)方面介紹Redis持久化機(jī)制:

Redis經(jīng)典技巧之詳解持久化原理

本文從整體上詳細(xì)介紹Redis的兩種持久化方式,包含工作原理、持久化流程及實(shí)踐策略,以及背后的一些理論知識(shí)。上一篇文章僅介紹了RDB持久化,但是Redis持久化是一個(gè)整體,單獨(dú)介紹不成體系,故重新整理。

Redis是一個(gè)內(nèi)存數(shù)據(jù)庫,所有的數(shù)據(jù)將保存在內(nèi)存中,這與傳統(tǒng)的MySQL、Oracle、SqlServer等關(guān)系型數(shù)據(jù)庫直接把數(shù)據(jù)保存到硬盤相比,Redis的讀寫效率非常高。但是保存在內(nèi)存中也有一個(gè)很大的缺陷,一旦斷電或者宕機(jī),內(nèi)存數(shù)據(jù)庫中的內(nèi)容將會(huì)全部丟失。為了彌補(bǔ)這一缺陷,Redis提供了把內(nèi)存數(shù)據(jù)持久化到硬盤文件,以及通過備份文件來恢復(fù)數(shù)據(jù)的功能,即Redis持久化機(jī)制。

Redis支持兩種方式的持久化:RDB快照和AOF。

RDB持久化

RDB快照用官方的話來說:RDB持久化方案是按照指定時(shí)間間隔對(duì)你的數(shù)據(jù)集生成的時(shí)間點(diǎn)快照(point-to-time snapshot)。它以緊縮的二進(jìn)制文件保存Redis數(shù)據(jù)庫某一時(shí)刻所有數(shù)據(jù)對(duì)象的內(nèi)存快照,可用于Redis的數(shù)據(jù)備份、轉(zhuǎn)移與恢復(fù)。到目前為止,仍是官方的默認(rèn)支持方案。

RDB工作原理

既然說RDB是Redis中數(shù)據(jù)集的時(shí)間點(diǎn)快照,那我們先簡單了解一下Redis內(nèi)的數(shù)據(jù)對(duì)象在內(nèi)存中是如何存儲(chǔ)與組織的。

默認(rèn)情況下,Redis中有16個(gè)數(shù)據(jù)庫,編號(hào)從0-15,每個(gè)Redis數(shù)據(jù)庫使用一個(gè)redisDb對(duì)象來表示,redisDb使用hashtable存儲(chǔ)K-V對(duì)象。為方便理解,我以其中一個(gè)db為例繪制Redis內(nèi)部數(shù)據(jù)的存儲(chǔ)結(jié)構(gòu)示意圖。 Redis經(jīng)典技巧之詳解持久化原理 時(shí)間點(diǎn)快照也就是某一時(shí)刻Redis內(nèi)每個(gè)DB中每個(gè)數(shù)據(jù)對(duì)象的狀態(tài),先假設(shè)在這一時(shí)刻所有的數(shù)據(jù)對(duì)象不再改變,我們就可以按照上圖中的數(shù)據(jù)結(jié)構(gòu)關(guān)系,把這些數(shù)據(jù)對(duì)象依次讀取出來并寫入到文件中,以此實(shí)現(xiàn)Redis的持久化。然后,當(dāng)Redis重啟時(shí)按照規(guī)則讀取這個(gè)文件中的內(nèi)容,再寫入到Redis內(nèi)存即可恢復(fù)至持久化時(shí)的狀態(tài)。

當(dāng)然,這個(gè)前提時(shí)我們上面的假設(shè)成立,否則面對(duì)一個(gè)時(shí)刻變化的數(shù)據(jù)集,我們無從下手。我們知道Redis中客戶端命令處理是單線程模型,如果把持久化作為一個(gè)命令處理,那數(shù)據(jù)集肯定時(shí)處于靜止?fàn)顟B(tài)。另外,操作系統(tǒng)提供的fork()函數(shù)創(chuàng)建的子進(jìn)程可獲得與父進(jìn)程一致的內(nèi)存數(shù)據(jù),相當(dāng)于獲取了內(nèi)存數(shù)據(jù)副本;fork完成后,父進(jìn)程該干嘛干嘛,持久化狀態(tài)的工作交給子進(jìn)程就行了。

很顯然,第一種情況不可取,持久化備份會(huì)導(dǎo)致短時(shí)間內(nèi)Redis服務(wù)不可用,這對(duì)于高HA的系統(tǒng)來講是無法容忍的。所以,第二種方式是RDB持久化的主要實(shí)踐方式。由于fork子進(jìn)程后,父進(jìn)程數(shù)據(jù)一直在變化,子進(jìn)程并不與父進(jìn)程同步,RDB持久化必然無法保證實(shí)時(shí)性;RDB持久化完成后發(fā)生斷電或宕機(jī),會(huì)導(dǎo)致部分?jǐn)?shù)據(jù)丟失;備份頻率決定了丟失數(shù)據(jù)量的大小,提高備份頻率,意味著fork過程消耗較多的CPU資源,也會(huì)導(dǎo)致較大的磁盤I/O。

持久化流程

在Redis內(nèi)完成RDB持久化的方法有rdbSave和rdbSaveBackground兩個(gè)函數(shù)方法(源碼文件rdb.c中),先簡單說下兩者差別:

  • rdbSave:是同步執(zhí)行的,方法調(diào)用后就會(huì)立刻啟動(dòng)持久化流程。由于Redis是單線程模型,持久化過程中會(huì)阻塞,Redis無法對(duì)外提供服務(wù);

  • rdbSaveBackground:是后臺(tái)(異步)執(zhí)行的,該方法會(huì)fork出子進(jìn)程,真正的持久化過程是在子進(jìn)程中執(zhí)行的(調(diào)用rdbSave),主進(jìn)程會(huì)繼續(xù)提供服務(wù);

RDB持久化的觸發(fā)必然離不開以上兩個(gè)方法,觸發(fā)的方式分為手動(dòng)和自動(dòng)。手動(dòng)觸發(fā)容易理解,是指我們通過Redis客戶端人為的對(duì)Redis服務(wù)端發(fā)起持久化備份指令,然后Redis服務(wù)端開始執(zhí)行持久化流程,這里的指令有save和bgsave。自動(dòng)觸發(fā)是Redis根據(jù)自身運(yùn)行要求,在滿足預(yù)設(shè)條件時(shí)自動(dòng)觸發(fā)的持久化流程,自動(dòng)觸發(fā)的場(chǎng)景有如下幾個(gè)(摘自這篇文章):

  • serverCron中save m n配置規(guī)則自動(dòng)觸發(fā);

  • 從節(jié)點(diǎn)全量復(fù)制時(shí),主節(jié)點(diǎn)發(fā)送rdb文件給從節(jié)點(diǎn)完成復(fù)制操作,主節(jié)點(diǎn)會(huì)出發(fā)bgsave;

  • 執(zhí)行debug reload命令重新加載redis時(shí);

  • 默認(rèn)情況下(未開啟AOF)執(zhí)行shutdown命令時(shí),自動(dòng)執(zhí)行bgsave;

結(jié)合源碼及參考文章,我整理了RDB持久化流程來幫助大家有個(gè)整體的了解,然后再從一些細(xì)節(jié)進(jìn)行說明。 Redis經(jīng)典技巧之詳解持久化原理 從上圖可以知道:

  • 自動(dòng)觸發(fā)的RDB持久化是通過rdbSaveBackground以子進(jìn)程方式執(zhí)行的持久化策略;

  • 手動(dòng)觸發(fā)是以客戶端命令方式觸發(fā)的,包含save和bgsave兩個(gè)命令,其中save命令是在Redis的命令處理線程以阻塞的方式調(diào)用rdbSave方法完成的。

自動(dòng)觸發(fā)流程是一個(gè)完整的鏈路,涵蓋了rdbSaveBackground、rdbSave等,接下來我以serverCron為例分析一下整個(gè)流程。

save規(guī)則及檢查

serverCron是Redis內(nèi)的一個(gè)周期性函數(shù),每隔100毫秒執(zhí)行一次,它的其中一項(xiàng)工作就是:根據(jù)配置文件中save規(guī)則來判斷當(dāng)前需要進(jìn)行自動(dòng)持久化流程,如果滿足條件則嘗試開始持久化。了解一下這部分的實(shí)現(xiàn)。

redisServer中有幾個(gè)與RDB持久化有關(guān)的字段,我從代碼中摘出來,中英文對(duì)照著看下:

struct redisServer {  /* 省略其他字段 */      /* RDB persistence */     long long dirty;                /* Changes to DB from the last save              * 上次持久化后修改key的次數(shù) */     struct saveparam *saveparams;   /* Save points array for RDB,              * 對(duì)應(yīng)配置文件多個(gè)save參數(shù) */     int saveparamslen;              /* Number of saving points,              * save參數(shù)的數(shù)量 */     time_t lastsave;                /* Unix time of last successful save               * 上次持久化時(shí)間*/     /* 省略其他字段 */ }  /* 對(duì)應(yīng)redis.conf中的save參數(shù) */ struct saveparam {     time_t seconds;     /* 統(tǒng)計(jì)時(shí)間范圍 */        int changes;     /* 數(shù)據(jù)修改次數(shù) */ };

saveparams對(duì)應(yīng)redis.conf下的save規(guī)則,save參數(shù)是Redis觸發(fā)自動(dòng)備份的觸發(fā)策略,seconds為統(tǒng)計(jì)時(shí)間(單位:秒), changes為在統(tǒng)計(jì)時(shí)間內(nèi)發(fā)生寫入的次數(shù)。save m n的意思是:m秒內(nèi)有n條寫入就觸發(fā)一次快照,即備份一次。save參數(shù)可以配置多組,滿足在不同條件的備份要求。如果需要關(guān)閉RDB的自動(dòng)備份策略,可以使用save ""。以下為幾種配置的說明:

# 表示900秒(15分鐘)內(nèi)至少有1個(gè)key的值發(fā)生變化,則執(zhí)行 save 900 1 # 表示300秒(5分鐘)內(nèi)至少有1個(gè)key的值發(fā)生變化,則執(zhí)行 save 300 10 # 表示60秒(1分鐘)內(nèi)至少有10000個(gè)key的值發(fā)生變化,則執(zhí)行 save 60 10000 # 該配置將會(huì)關(guān)閉RDB方式的持久化 save ""

serverCron對(duì)RDB save規(guī)則的檢測(cè)代碼如下所示:

int serverCron(struct aeEventLoop *eventLoop, long long id, void *clientData) {     /* 省略其他邏輯 */          /* 如果用戶請(qǐng)求進(jìn)行AOF文件重寫時(shí),Redis正在執(zhí)行RDB持久化,Redis會(huì)安排在RDB持久化完成后執(zhí)行AOF文件重寫,      * 如果aof_rewrite_scheduled為true,說明需要執(zhí)行用戶的請(qǐng)求 */     /* Check if a background saving or AOF rewrite in progress terminated. */     if (hasActiveChildProcess() || ldbPendingChildren())     {         run_with_period(1000) receiveChildInfo();         checkChildrenDone();     } else {         /* 后臺(tái)無 saving/rewrite 子進(jìn)程才會(huì)進(jìn)行,逐個(gè)檢查每個(gè)save規(guī)則*/         for (j = 0; j < server.saveparamslen; j++) {             struct saveparam *sp = server.saveparams+j;                          /* 檢查規(guī)則有幾個(gè):滿足修改次數(shù),滿足統(tǒng)計(jì)周期,達(dá)到重試時(shí)間間隔或者上次持久化完成*/             if (server.dirty >= sp->changes                  && server.unixtime-server.lastsave > sp->seconds                  &&(server.unixtime-server.lastbgsave_try > CONFIG_BGSAVE_RETRY_DELAY || server.lastbgsave_status == C_OK))             {                 serverLog(LL_NOTICE,"%d changes in %d seconds. Saving...", sp->changes, (int)sp->seconds);                 rdbSaveInfo rsi, *rsiptr;                 rsiptr = rdbPopulateSaveInfo(&rsi);                 /* 執(zhí)行bgsave過程 */                 rdbSaveBackground(server.rdb_filename,rsiptr);                 break;             }         }          /* 省略:Trigger an AOF rewrite if needed. */     }  /* 省略其他邏輯 */ }

如果沒有后臺(tái)的RDB持久化或AOF重寫進(jìn)程,serverCron會(huì)根據(jù)以上配置及狀態(tài)判斷是否需要執(zhí)行持久化操作,判斷依據(jù)就是看lastsave、dirty是否滿足saveparams數(shù)組中的其中一個(gè)條件。如果有一個(gè)條件匹配,則調(diào)用rdbSaveBackground方法,執(zhí)行異步持久化流程。

rdbSaveBackground

rdbSaveBackground是RDB持久化的輔助性方法,主要工作是fork子進(jìn)程,然后根據(jù)調(diào)用方(父進(jìn)程或者子進(jìn)程)不同,有兩種不同的執(zhí)行邏輯。

  • 如果調(diào)用方是父進(jìn)程,則fork出子進(jìn)程,保存子進(jìn)程信息后直接返回。

  • 如果調(diào)用方是子進(jìn)程則調(diào)用rdbSave執(zhí)行RDB持久化邏輯,持久化完成后退出子進(jìn)程。

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);      // fork子進(jìn)程     if ((childpid = redisFork(CHILD_TYPE_RDB)) == 0) {         int retval;          /* Child 子進(jìn)程:修改進(jìn)程標(biāo)題 */         redisSetProcTitle("redis-rdb-bgsave");         redisSetCpuAffinity(server.bgsave_cpulist);         // 執(zhí)行rdb持久化         retval = rdbSave(filename,rsi);         if (retval == C_OK) {             sendChildCOWInfo(CHILD_TYPE_RDB, 1, "RDB");         }         // 持久化完成后,退出子進(jìn)程         exitFromChild((retval == C_OK) ? 0 : 1);     } else {         /* Parent 父進(jìn)程:記錄fork子進(jìn)程的時(shí)間等信息*/         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);         // 記錄子進(jìn)程開始的時(shí)間、類型等。         server.rdb_save_time_start = time(NULL);         server.rdb_child_type = RDB_CHILD_TYPE_DISK;         return C_OK;     }     return C_OK; /* unreached */ }

rdbSave是真正執(zhí)行持久化的方法,它在執(zhí)行時(shí)存在大量的I/O、計(jì)算操作,耗時(shí)、CPU占用較大,在Redis的單線程模型中持久化過程會(huì)持續(xù)占用線程資源,進(jìn)而導(dǎo)致Redis無法提供其他服務(wù)。為了解決這一問題Redis在rdbSaveBackground中fork出子進(jìn)程,由子進(jìn)程完成持久化工作,避免了占用父進(jìn)程過多的資源。

需要注意的是,如果父進(jìn)程內(nèi)存占用過大,fork過程會(huì)比較耗時(shí),在這個(gè)過程中父進(jìn)程無法對(duì)外提供服務(wù);另外,需要綜合考慮計(jì)算機(jī)內(nèi)存使用量,fork子進(jìn)程后會(huì)占用雙倍的內(nèi)存資源,需要確保內(nèi)存夠用。通過info stats命令查看latest_fork_usec選項(xiàng),可以獲取最近一個(gè)fork以操作的耗時(shí)。

rdbSave

Redis的rdbSave函數(shù)是真正進(jìn)行RDB持久化的函數(shù),流程、細(xì)節(jié)賊多,整體流程可以總結(jié)為:創(chuàng)建并打開臨時(shí)文件、Redis內(nèi)存數(shù)據(jù)寫入臨時(shí)文件、臨時(shí)文件寫入磁盤、臨時(shí)文件重命名為正式RDB文件、更新持久化狀態(tài)信息(dirty、lastsave)。其中“Redis內(nèi)存數(shù)據(jù)寫入臨時(shí)文件”最為核心和復(fù)雜,寫入過程直接體現(xiàn)了RDB文件的文件格式,本著一圖勝千言的理念,我按照源碼流程繪制了下圖。 Redis經(jīng)典技巧之詳解持久化原理 補(bǔ)充說明一下,上圖右下角“遍歷當(dāng)前數(shù)據(jù)庫的鍵值對(duì)并寫入”這個(gè)環(huán)節(jié)會(huì)根據(jù)不同類型的Redis數(shù)據(jù)類型及底層數(shù)據(jù)結(jié)構(gòu)采用不同的格式寫入到RDB文件中,不再展開了。我覺得大家對(duì)整個(gè)過程有個(gè)直觀的理解就好,這對(duì)于我們理解Redis內(nèi)部的運(yùn)作機(jī)制大有裨益。

AOF持久化

上一節(jié)我們知道RDB是一種時(shí)間點(diǎn)(point-to-time)快照,適合數(shù)據(jù)備份及災(zāi)難恢復(fù),由于工作原理的“先天性缺陷”無法保證實(shí)時(shí)性持久化,這對(duì)于緩存丟失零容忍的系統(tǒng)來說是個(gè)硬傷,于是就有了AOF。

AOF工作原理

AOF是Append Only File的縮寫,它是Redis的完全持久化策略,從1.1版本開始支持;這里的file存儲(chǔ)的是引起Redis數(shù)據(jù)修改的命令集合(比如:set/hset/del等),這些集合按照Redis Server的處理順序追加到文件中。當(dāng)重啟Redis時(shí),Redis就可以從頭讀取AOF中的指令并重放,進(jìn)而恢復(fù)關(guān)閉前的數(shù)據(jù)狀態(tài)。

AOF持久化默認(rèn)是關(guān)閉的,修改redis.conf以下信息并重啟,即可開啟AOF持久化功能。

# no-關(guān)閉,yes-開啟,默認(rèn)no appendonly yes appendfilename appendonly.aof

AOF本質(zhì)是為了持久化,持久化對(duì)象是Redis內(nèi)每一個(gè)key的狀態(tài),持久化的目的是為了在Reids發(fā)生故障重啟后能夠恢復(fù)至重啟前或故障前的狀態(tài)。相比于RDB,AOF采取的策略是按照?qǐng)?zhí)行順序持久化每一條能夠引起Redis中對(duì)象狀態(tài)變更的命令,命令是有序的、有選擇的。把a(bǔ)of文件轉(zhuǎn)移至任何一臺(tái)Redis Server,從頭到尾按序重放這些命令即可恢復(fù)如初。舉個(gè)例子:

首先執(zhí)行指令set number 0,然后隨機(jī)調(diào)用incr numberget number 各5次,最后再執(zhí)行一次get number ,我們得到的結(jié)果肯定是5。

因?yàn)樵谶@個(gè)過程中,能夠引起number狀態(tài)變更的只有set/incr類型的指令,并且它們執(zhí)行的先后順序是已知的,無論執(zhí)行多少次get都不會(huì)影響number的狀態(tài)。所以,保留所有set/incr命令并持久化至aof文件即可。按照aof的設(shè)計(jì)原理,aof文件中的內(nèi)容應(yīng)該是這樣的(這里是假設(shè),實(shí)際為RESP協(xié)議):

set number 0 incr number incr number incr number incr number incr number

最本質(zhì)的原理用“命令重放”四個(gè)字就可以概括。但是,考慮實(shí)際生產(chǎn)環(huán)境的復(fù)雜性及操作系統(tǒng)等方面的限制,Redis所要考慮的工作要比這個(gè)例子復(fù)雜的多:

  • Redis Server啟動(dòng)后,aof文件一直在追加命令,文件會(huì)越來越大。文件越大,Redis重啟后恢復(fù)耗時(shí)越久;文件太大,轉(zhuǎn)移工作就越難;不加管理,可能撐爆硬盤。很顯然,需要在合適的時(shí)機(jī)對(duì)文件進(jìn)行精簡。例子中的5條incr指令很明顯的可以替換為為一條set命令,存在很大的壓縮空間。

  • 眾所周知,文件I/O是操作系統(tǒng)性能的短板,為了提高效率,文件系統(tǒng)設(shè)計(jì)了一套復(fù)雜的緩存機(jī)制,Redis操作命令的追加操作只是把數(shù)據(jù)寫入了緩沖區(qū)(aof_buf),從緩沖區(qū)到寫入物理文件在性能與安全之間權(quán)衡會(huì)有不同的選擇。

  • 文件壓縮即意味著重寫,重寫時(shí)即可依據(jù)已有的aof文件做命令整合,也可以先根據(jù)當(dāng)前Redis內(nèi)數(shù)據(jù)的狀態(tài)做快照,再把存儲(chǔ)快照過程中的新增的命令做追加。

  • aof備份后的文件是為了恢復(fù)數(shù)據(jù),結(jié)合aof文件的格式、完整性等因素,Redis也要設(shè)計(jì)一套完整的方案做支持。

持久化流程

從流程上來看,AOF的工作原理可以概括為幾個(gè)步驟:命令追加(append)、文件寫入與同步(fsync)、文件重寫(rewrite)、重啟加載(load),接下來依次了解每個(gè)步驟的細(xì)節(jié)及背后的設(shè)計(jì)哲學(xué)。 Redis經(jīng)典技巧之詳解持久化原理

命令追加

當(dāng) AOF 持久化功能處于打開狀態(tài)時(shí),Redis 在執(zhí)行完一個(gè)寫命令之后,會(huì)以協(xié)議格式(也就是RESP,即 Redis 客戶端和服務(wù)器交互的通信協(xié)議 )把被執(zhí)行的寫命令追加到 Redis 服務(wù)端維護(hù)的 AOF 緩沖區(qū)末尾。對(duì)AOF文件只有單線程的追加操作,沒有seek等復(fù)雜的操作,即使斷電或宕機(jī)也不存在文件損壞風(fēng)險(xiǎn)。另外,使用文本協(xié)議好處多多:

  • 文本協(xié)議有很好的兼容性;

  • 文本協(xié)議就是客戶端的請(qǐng)求命令,不需要二次處理,節(jié)省了存儲(chǔ)及加載時(shí)的處理開銷;

  • 文本協(xié)議具有可讀性,方便查看、修改等處理。

AOF緩沖區(qū)類型為Redis自主設(shè)計(jì)的數(shù)據(jù)結(jié)構(gòu)sds,Redis會(huì)根據(jù)命令的類型采用不同的方法(catAppendOnlyGenericCommandcatAppendOnlyExpireAtCommand等)對(duì)命令內(nèi)容進(jìn)行處理,最后寫入緩沖區(qū)。

需要注意的是:如果命令追加時(shí)正在進(jìn)行AOF重寫,這些命令還會(huì)追加到重寫緩沖區(qū)(aof_rewrite_buffer)。

文件寫入與同步

AOF文件的寫入與同步離不開操作系統(tǒng)的支持,開始介紹之前,我們需要補(bǔ)充一下Linux I/O緩沖區(qū)相關(guān)知識(shí)。硬盤I/O性能較差,文件讀寫速度遠(yuǎn)遠(yuǎn)比不上CPU的處理速度,如果每次文件寫入都等待數(shù)據(jù)寫入硬盤,會(huì)整體拉低操作系統(tǒng)的性能。為了解決這個(gè)問題,操作系統(tǒng)提供了延遲寫(delayed write)機(jī)制來提高硬盤的I/O性能。 Redis經(jīng)典技巧之詳解持久化原理

傳統(tǒng)的UNIX實(shí)現(xiàn)在內(nèi)核中設(shè)有緩沖區(qū)高速緩存或頁面高速緩存,大多數(shù)磁盤I/O都通過緩沖進(jìn)行。 當(dāng)將數(shù)據(jù)寫入文件時(shí),內(nèi)核通常先將該數(shù)據(jù)復(fù)制到其中一個(gè)緩沖區(qū)中,如果該緩沖區(qū)尚未寫滿,則并不將其排入輸出隊(duì)列,而是等待其寫滿或者當(dāng)內(nèi)核需要重用該緩沖區(qū)以便存放其他磁盤塊數(shù)據(jù)時(shí), 再將該緩沖排入到輸出隊(duì)列,然后待其到達(dá)隊(duì)首時(shí),才進(jìn)行實(shí)際的I/O操作。這種輸出方式就被稱為延遲寫。

延遲寫減少了磁盤讀寫次數(shù),但是卻降低了文件內(nèi)容的更新速度,使得欲寫到文件中的數(shù)據(jù)在一段時(shí)間內(nèi)并沒有寫到磁盤上。當(dāng)系統(tǒng)發(fā)生故障時(shí),這種延遲可能造成文件更新內(nèi)容的丟失。為了保證磁盤上實(shí)際文件系統(tǒng)與緩沖區(qū)高速緩存中內(nèi)容的一致性,UNIX系統(tǒng)提供了sync、fsync和fdatasync三個(gè)函數(shù)為強(qiáng)制寫入硬盤提供支持。

Redis每次事件輪訓(xùn)結(jié)束前(beforeSleep)都會(huì)調(diào)用函數(shù)flushAppendOnlyFileflushAppendOnlyFile會(huì)把AOF緩沖區(qū)(aof_buf)中的數(shù)據(jù)寫入內(nèi)核緩沖區(qū),并且根據(jù)appendfsync配置來決定采用何種策略把內(nèi)核緩沖區(qū)中的數(shù)據(jù)寫入磁盤,即調(diào)用fsync()。該配置有三個(gè)可選項(xiàng)alwaysnoeverysec,具體說明如下:

  • always:每次都調(diào)用fsync(),是安全性最高、性能最差的一種策略。

  • no:不會(huì)調(diào)用fsync()。性能最好,安全性最差。

  • everysec:僅在滿足同步條件時(shí)調(diào)用fsync()。這是官方建議的同步策略,也是默認(rèn)配置,做到兼顧性能和數(shù)據(jù)安全性,理論上只有在系統(tǒng)突然宕機(jī)的情況下丟失1秒的數(shù)據(jù)。

注意:上面介紹的策略受配置項(xiàng)no-appendfsync-on-rewrite的影響,它的作用是告知Redis:AOF文件重寫期間是否禁止調(diào)用fsync(),默認(rèn)是no。

如果appendfsync設(shè)置為alwayseverysec,后臺(tái)正在進(jìn)行的BGSAVE或者BGREWRITEAOF消耗過多的磁盤I/O,在某些Linux系統(tǒng)配置下,Redis對(duì)fsync()的調(diào)用可能阻塞很長時(shí)間。然而這個(gè)問題還沒有修復(fù),因?yàn)榧词故窃诓煌木€程中執(zhí)行fsync(),同步寫入操作也會(huì)被阻塞。

為了緩解此問題,可以使用該選項(xiàng),以防止在進(jìn)行BGSAVEBGREWRITEAOF時(shí)在主進(jìn)程中調(diào)用fsync()。

  • 設(shè)置為yes意味著,如果子進(jìn)程正在進(jìn)行BGSAVEBGREWRITEAOF,AOF的持久化能力就與appendfsync設(shè)置為no有著相同的效果。最糟糕的情況下,這可能會(huì)導(dǎo)致30秒的緩存數(shù)據(jù)丟失。

  • 如果你的系統(tǒng)有上面描述的延遲問題,就把這個(gè)選項(xiàng)設(shè)置為yes,否則保持為no

文件重寫

如前面提到的,Redis長時(shí)間運(yùn)行,命令不斷寫入AOF,文件會(huì)越來越大,不加控制可能影響宿主機(jī)的安全。

為了解決AOF文件體積問題,Redis引入了AOF文件重寫功能,它會(huì)根據(jù)Redis內(nèi)數(shù)據(jù)對(duì)象的最新狀態(tài)生成新的AOF文件,新舊文件對(duì)應(yīng)的數(shù)據(jù)狀態(tài)一致,但是新文件會(huì)具有較小的體積。重寫既減少了AOF文件對(duì)磁盤空間的占用,又可以提高Redis重啟時(shí)數(shù)據(jù)恢復(fù)的速度。還是下面這個(gè)例子,舊文件中的6條命令等同于新文件中的1條命令,壓縮效果顯而易見。 Redis經(jīng)典技巧之詳解持久化原理我們說,AOF文件太大時(shí)會(huì)觸發(fā)AOF文件重寫,那到底是多大呢?有哪些情況會(huì)觸發(fā)重寫操作呢? ** 與RDB方式一樣,AOF文件重寫既可以手動(dòng)觸發(fā),也會(huì)自動(dòng)觸發(fā)。手動(dòng)觸發(fā)直接調(diào)用bgrewriteaof命令,如果當(dāng)時(shí)無子進(jìn)程執(zhí)行會(huì)立刻執(zhí)行,否則安排在子進(jìn)程結(jié)束后執(zhí)行。自動(dòng)觸發(fā)由Redis的周期性方法serverCron檢查在滿足一定條件時(shí)觸發(fā)。先了解兩個(gè)配置項(xiàng):

  • auto-aof-rewrite-percentage:代表當(dāng)前AOF文件大小(aof_current_size)和上一次重寫后AOF文件大小(aof_base_size)相比,增長的比例。

  • auto-aof-rewrite-min-size:表示運(yùn)行BGREWRITEAOF時(shí)AOF文件占用空間最小值,默認(rèn)為64MB;

Redis啟動(dòng)時(shí)把aof_base_size初始化為當(dāng)時(shí)aof文件的大小,Redis運(yùn)行過程中,當(dāng)AOF文件重寫操作完成時(shí),會(huì)對(duì)其進(jìn)行更新;aof_current_sizeserverCron執(zhí)行時(shí)AOF文件的實(shí)時(shí)大小。當(dāng)滿足以下兩個(gè)條件時(shí),AOF文件重寫就會(huì)觸發(fā):

增長比例:(aof_current_size - aof_base_size) / aof_base_size > auto-aof-rewrite-percentage 文件大小:aof_current_size > auto-aof-rewrite-min-size

手動(dòng)觸發(fā)與自動(dòng)觸發(fā)的代碼如下,同樣在周期性方法serverCron中:

int serverCron(struct aeEventLoop *eventLoop, long long id, void *clientData) {     /* 省略其他邏輯 */          /* 如果用戶請(qǐng)求進(jìn)行AOF文件重寫時(shí),Redis正在執(zhí)行RDB持久化,Redis會(huì)安排在RDB持久化完成后執(zhí)行AOF文件重寫,      * 如果aof_rewrite_scheduled為true,說明需要執(zhí)行用戶的請(qǐng)求 */     if (!hasActiveChildProcess() &&         server.aof_rewrite_scheduled)     {         rewriteAppendOnlyFileBackground();     }      /* Check if a background saving or AOF rewrite in progress terminated. */     if (hasActiveChildProcess() || ldbPendingChildren())     {         run_with_period(1000) receiveChildInfo();         checkChildrenDone();     } else {         /* 省略rdb持久化條件檢查 */          /* AOF重寫條件檢查:aof開啟、無子進(jìn)程運(yùn)行、增長百分比已設(shè)置、當(dāng)前文件大小超過閾值 */         if (server.aof_state == AOF_ON &&             !hasActiveChildProcess() &&             server.aof_rewrite_perc &&             server.aof_current_size > server.aof_rewrite_min_size)         {             long long base = server.aof_rewrite_base_size ?                 server.aof_rewrite_base_size : 1;             /* 計(jì)算增長百分比 */             long long growth = (server.aof_current_size*100/base) - 100;             if (growth >= server.aof_rewrite_perc) {                 serverLog(LL_NOTICE,"Starting automatic rewriting of AOF on %lld%% growth",growth);                 rewriteAppendOnlyFileBackground();             }         }     }     /**/ }

AOF文件重寫的流程是什么?聽說Redis支持混合持久化,對(duì)AOF文件重寫有什么影響?

從4.0版本開始,Redis在AOF模式中引入了混合持久化方案,即:純AOF方式、RDB+AOF方式,這一策略由配置參數(shù)aof-use-rdb-preamble(使用RDB作為AOF文件的前半段)控制,默認(rèn)關(guān)閉(no),設(shè)置為yes可開啟。所以,在AOF重寫過程中文件的寫入會(huì)有兩種不同的方式。當(dāng)aof-use-rdb-preamble的值是:

  • no:按照AOF格式寫入命令,與4.0前版本無差別;

  • yes:先按照RDB格式寫入數(shù)據(jù)狀態(tài),然后把重寫期間AOF緩沖區(qū)的內(nèi)容以AOF格式寫入,文件前半部分為RDB格式,后半部分為AOF格式。

結(jié)合源碼(6.0版本,源碼太多這里不貼出,可參考aof.c)及參考資料,繪制AOF重寫(BGREWRITEAOF)流程圖: Redis經(jīng)典技巧之詳解持久化原理 結(jié)合上圖,總結(jié)一下AOF文件重寫的流程:

  • rewriteAppendOnlyFileBackground開始執(zhí)行,檢查是否有正在進(jìn)行的AOF重寫或RDB持久化子進(jìn)程:如果有,則退出該流程;如果沒有,則繼續(xù)創(chuàng)建接下來父子進(jìn)程間數(shù)據(jù)傳輸?shù)耐ㄐ殴艿馈?zhí)行fork()操作,成功后父子進(jìn)程分別執(zhí)行不同的流程。

  • 父進(jìn)程:
    • 記錄子進(jìn)程信息(pid)、時(shí)間戳等;

    • 繼續(xù)響應(yīng)其他客戶端請(qǐng)求;

    • 收集AOF重寫期間的命令,追加至aof_rewrite_buffer;

    • 等待并向子進(jìn)程同步aof_rewrite_buffer的內(nèi)容;

  • 子進(jìn)程:
    • 修改當(dāng)前進(jìn)程名稱,創(chuàng)建重寫所需的臨時(shí)文件,調(diào)用rewriteAppendOnlyFile函數(shù);

    • 根據(jù)aof-use-rdb-preamble配置,以RDB或AOF方式寫入前半部分,并同步至硬盤;

    • 從父進(jìn)程接收增量AOF命令,以AOF方式寫入后半部分,并同步至硬盤;

    • 重命名AOF文件,子進(jìn)程退出。

數(shù)據(jù)加載

Redis啟動(dòng)后通過loadDataFromDisk函數(shù)執(zhí)行數(shù)據(jù)加載工作。這里需要注意,雖然持久化方式可以選擇AOF、RDB或者兩者兼用,但是數(shù)據(jù)加載時(shí)必須做出選擇,兩種方式各自加載一遍就亂套了。

理論上,AOF持久化比RDB具有更好的實(shí)時(shí)性,當(dāng)開啟了AOF持久化方式,Redis在數(shù)據(jù)加載時(shí)優(yōu)先考慮AOF方式。而且,Redis 4.0版本后AOF支持了混合持久化,加載AOF文件需要考慮版本兼容性。Redis數(shù)據(jù)加載流程如下圖所示: Redis經(jīng)典技巧之詳解持久化原理 在AOF方式下,開啟混合持久化機(jī)制生成的文件是“RDB頭+AOF尾”,未開啟時(shí)生成的文件全部為AOF格式。考慮兩種文件格式的兼容性,如果Redis發(fā)現(xiàn)AOF文件為RDB頭,會(huì)使用RDB數(shù)據(jù)加載的方法讀取并恢復(fù)前半部分;然后再使用AOF方式讀取并恢復(fù)后半部分。由于AOF格式存儲(chǔ)的數(shù)據(jù)為RESP協(xié)議命令,Redis采用偽客戶端執(zhí)行命令的方式來恢復(fù)數(shù)據(jù)。

如果在AOF命令追加過程中發(fā)生宕機(jī),由于延遲寫的技術(shù)特點(diǎn),AOF的RESP命令可能不完整(被截?cái)啵S龅竭@種情況時(shí),Redis會(huì)按照配置項(xiàng)aof-load-truncated執(zhí)行不同的處理策略。這個(gè)配置是告訴Redis啟動(dòng)時(shí)讀取aof文件,如果發(fā)現(xiàn)文件被截?cái)啵ú煌暾r(shí)該如何處理:

  • yes:則盡可能多的加載數(shù)據(jù),并以日志的方式通知用戶;

  • no:則以系統(tǒng)錯(cuò)誤的方式崩潰,并禁止啟動(dòng),需要用戶修復(fù)文件后再重啟。

總結(jié)

Redis提供了兩種持久化的選擇:RDB支持以特定的實(shí)踐間隔為數(shù)據(jù)集生成時(shí)間點(diǎn)快照;AOF把Redis Server收到的每條寫指令持久化到日志中,待Redis重啟時(shí)通過重放命令恢復(fù)數(shù)據(jù)。日志格式為RESP協(xié)議,對(duì)日志文件只做append操作,無損壞風(fēng)險(xiǎn)。并且當(dāng)AOF文件過大時(shí)可以自動(dòng)重寫壓縮文件。

當(dāng)然,如果你不需要對(duì)數(shù)據(jù)進(jìn)行持久化,也可以禁用Redis的持久化功能,但是大多數(shù)情況并非如此。實(shí)際上,我們時(shí)有可能同時(shí)使用RDB和AOF兩種方式的,最重要的就是我們要理解兩者的區(qū)別,以便合理使用。

RDB vs AOF

RDB優(yōu)點(diǎn)

  • RDB是一個(gè)緊湊壓縮的二進(jìn)制文件,代表Redis在某一個(gè)時(shí)間點(diǎn)上的數(shù)據(jù)快照,非常適合用于備份、全量復(fù)制等場(chǎng)景。

  • RDB對(duì)災(zāi)難恢復(fù)、數(shù)據(jù)遷移非常友好,RDB文件可以轉(zhuǎn)移至任何需要的地方并重新加載。

  • RDB是Redis數(shù)據(jù)的內(nèi)存快照,數(shù)據(jù)恢復(fù)速度較快,相比于AOF的命令重放有著更高的性能。

RDB缺點(diǎn)

  • RDB方式無法做到實(shí)時(shí)或秒級(jí)持久化。因?yàn)槌志没^程是通過fork子進(jìn)程后由子進(jìn)程完成的,子進(jìn)程的內(nèi)存只是在fork操作那一時(shí)刻父進(jìn)程的數(shù)據(jù)快照,而fork操作后父進(jìn)程持續(xù)對(duì)外服務(wù),內(nèi)部數(shù)據(jù)時(shí)刻變更,子進(jìn)程的數(shù)據(jù)不再更新,兩者始終存在差異,所以無法做到實(shí)時(shí)性。

  • RDB持久化過程中的fork操作,會(huì)導(dǎo)致內(nèi)存占用加倍,而且父進(jìn)程數(shù)據(jù)越多,fork過程越長。

  • Redis請(qǐng)求高并發(fā)可能會(huì)頻繁命中save規(guī)則,導(dǎo)致fork操作及持久化備份的頻率不可控;

  • RDB文件有文件格式要求,不同版本的Redis會(huì)對(duì)文件格式進(jìn)行調(diào)整,存在老版本無法兼容新版本的問題。

AOF優(yōu)點(diǎn)

  • AOF持久化有更好的實(shí)時(shí)性,我們可以選擇三種不同的方式(appendfsync):no、every second、always,every second作為默認(rèn)的策略具有最好的性能,極端情況下可能會(huì)丟失一秒的數(shù)據(jù)。

  • AOF文件只有append操作,無復(fù)雜的seek等文件操作,沒有損壞風(fēng)險(xiǎn)。即使最后寫入數(shù)據(jù)被截?cái)啵埠苋菀资褂?code>redis-check-aof工具修復(fù);

  • 當(dāng)AOF文件變大時(shí),Redis可在后臺(tái)自動(dòng)重寫。重寫過程中舊文件會(huì)持續(xù)寫入,重寫完成后新文件將變得更小,并且重寫過程中的增量命令也會(huì)append到新文件。

  • AOF文件以已于理解與解析的方式包含了對(duì)Redis中數(shù)據(jù)的所有操作命令。即使不小心錯(cuò)誤的清除了所有數(shù)據(jù),只要沒有對(duì)AOF文件重寫,我們就可以通過移除最后一條命令找回所有數(shù)據(jù)。

  • AOF已經(jīng)支持混合持久化,文件大小可以有效控制,并提高了數(shù)據(jù)加載時(shí)的效率。

AOF缺點(diǎn)

  • 對(duì)于相同的數(shù)據(jù)集合,AOF文件通常會(huì)比RDB文件大;

  • 在特定的fsync策略下,AOF會(huì)比RDB略慢。一般來講,fsync_every_second的性能仍然很高,fsync_no的性能與RDB相當(dāng)。但是在巨大的寫壓力下,RDB更能提供最大的低延時(shí)保障。

  • 在AOF上,Redis曾經(jīng)遇到一些幾乎不可能在RDB上遇到的罕見bug。一些特殊的指令(如BRPOPLPUSH)導(dǎo)致重新加載的數(shù)據(jù)與持久化之前不一致,Redis官方曾經(jīng)在相同的條件下進(jìn)行測(cè)試,但是無法復(fù)現(xiàn)問題。

使用建議

對(duì)RDB和AOF兩種持久化方式的工作原理、執(zhí)行流程及優(yōu)缺點(diǎn)了解后,我們來思考下,實(shí)際場(chǎng)景中應(yīng)該怎么權(quán)衡利弊,合理的使用兩種持久化方式。如果僅僅是使用Redis作為緩存工具,所有數(shù)據(jù)可以根據(jù)持久化數(shù)據(jù)庫進(jìn)行重建,則可關(guān)閉持久化功能,做好預(yù)熱、緩存穿透、擊穿、雪崩之類的防護(hù)工作即可。

一般情況下,Redis會(huì)承擔(dān)

贊(0)
分享到: 更多 (0)
?
網(wǎng)站地圖   滬ICP備18035694號(hào)-2    滬公網(wǎng)安備31011702889846號(hào)
99国产精品无码专区| 亚洲AV无码一区二区三区DV| 中文字幕久久久久人妻中出| 午夜亚洲国产理论片2020| 人人澡人人澡人人看添AV| 廖承宇做受被C22分钟视频| 国产性色ΑV视频免费| 丰满少妇被猛烈进入| 啊轻点灬大巴太粗太长视频 | AV网站免费线看| 曰本A级毛片无卡免费视频VA| 亚洲乱码日产精品BD在线观看| 午夜精品一区二区三区免费视频| 日韩欧美中文字幕看片你懂的| 欧美性受XXXX黑人XYX性爽| 免费久久人人爽人人爽AV| 久久水蜜桃亚洲AV无码精品 | 久爱WWW成人网免费视频| 99久久国产露脸国语对白| 国产AⅤ精品一区二区三区久久| 亚洲性色AV私人影院无码| 性高朝久久久久久久久久| 无码人妻精品一区二区蜜桃网站 | 把腿张开老子臊烂你妙妙| 99久久久国产精品免费| 干了老婆闺蜜两个小时| 炖肉计(是今)海棠| 父母儿女一家换着玩的文案| 狠狠综合久久综合88亚洲| 狠狠综合久久久久尤物丿| 男人边吃奶边挵进去呻吟动态图| 牛牛本精品99久久精品66| 女人的抉择全集免费播放| 男人J桶进女人J无遮挡| 熟女作爱一区二区视频| 无码人妻丰满熟妇区五十路| 亚洲熟妇无码爱V在线观看| 一本一道波多野结衣AV一区 | 无码人妻AⅤ一区二区三区| 亚洲综合激情七月婷婷| 柚子猫原神甘雨视内射频| 大肉大捧一进一出的视频| 国产AV无码日韩AV无码网站 | 少女たちよ在线观看动漫| 调教小SAO货撅起打屁股作文 | 特级西西人体444WWW高清大 | 国产精品午睡沙发系列| 国产日韩欧美亚欧在线| 国产在线超清日本一本| 精品免费AV一区二区三区| 久久久久无码精品国产AV蜜桃1| 老湿机影院免费观看| 熟妇的奶头又大又粗视频| 永久免费精品精品永久-夜色| 98久9在线 | 免费| 国产在线内射婷婷| 人妻在厨房被色诱 中文字幕| 试看120秒做受小视频免费| 伊人伊成久久人综合网| 国产精品久久久久精品三级APP| 国外B站推广网站| 久久亚洲国产成人精品性色 | 啊别插了视频髙清在线观看| 精品国精品国产自在久国产应用男| 久久九九久精品国产| 哦┅┅快┅┅用力啊┅┅动态图| 性色AV 一区二区三区| 啊灬啊别停灬用力啊公视频| 久久精品香蕉绿巨人登场| 无码人妻AV一二区二区三区 | 一区二区三区国产精品保安| 国产美女mv一区二区竹| 久久精品饰品有限公司网站 | 夜里十大禁用APP软件最新章节 | 男人躁女人到高潮视频| 亚洲AV无码一区二区三区鸳鸯影| 成人妇女免费播放久久久| 国产美女精品一区二区三区| 人妻无码一区二区三区久| 亚洲一区二区三区无码中文字幕 | 免费人成视频X8X8入口APP| 亚洲爆乳中文字幕无码专区网站 | 免费播放片高清在线视频| 亚洲成A人片在线播放| 18禁纯肉高黄无码动漫| 丰满饱满极品熟妇XXXX| 女朋友喊疼男生的心理活动知乎| 亚洲精品性爱av| 宝贝真乖四人一起NP| 国产亚洲AV☆浪潮在线观看| 日韩人妻系列无码专区| 337P粉嫩胞人体高清视频免费| 国产AV无码精品色午夜| 欧美日韩人妻精品一区二区在线| 无码人妻精品一区二区| 波多野结衣在线播放| 精品无码国产自产拍在线观看 | IJZZIJZZIJ亚洲大全| 国产成人亚洲精品另类动态图| 久久久久久老熟妇人妻av| 色综合天天视频在线观看| 亚洲中文字幕一区精品自拍| 国内精品久久久久久无码不卡| 欧美成人片一区二区三区| 亚洲AV成人一区二区三区观看在 | 在线 | 麻豆国产传媒| 大肉大捧一进一出的视频| 青青草无码精品伊人久久蜜臀| 亚洲国产成人久久一区二区三区 | 岳女二人名器共侍一夫的出处| 精品爆乳一区二区三区无码AV| 欧美一卡二卡三卡四卡视| 自拍日韩亚洲一区在线| 国产同性GV男男在线观看| 欧美性受XXXX人人本视频| 40岁成熟女人牲交片20分钟| 乱中年女人伦AV二区| 西西人体444www大胆无码视频| 国产成人亚洲精品无码青| 四虎成人精品无码| 国产成人无码AⅤ片在线观看你| 舌头伸入麻麻两腿之间 | 太大太长太粗太久太硬了| 工口全彩H肉无遮挡无翼乌| 色欲AV无码一区二区人妻| 关灯后在线高清免费观看| 天堂√中文最新版在线| 国产旡码高清一区二区三区 | 99国产精品无码| 欧洲洲一区二区精华液| 亚洲日本乱人伦片中文| 国产SUV精品一区二区6| 挺进绝色校花的紧窄小肉| 国产成人一区二区三区免费| 无码人妻丰满熟妇区毛片| 国产熟妇XXXXXⅩ性Ⅹ交| 亚洲AV综合色区无码一区偷拍 | 偷拍农村老熟妇XXXXX7视频| 国产精品无码AV一区二区三区| 欧美熟女一区二区三区| 被揉到高潮揉出水视频 | 高潮和狂野射精合集| 无码人妻精品一区二区三区久久 | 公交车后车座的疯狂的做| 四虎永久在线精品免费一区二区 | 亚洲日本一线产区二线区| 久久综合狠狠色综合伊人| 337P日本欧洲亚洲大胆在线 | 成人免费无码H黄网站WWW| 天美传媒蜜桃传媒精东| 国产亚洲成AⅤ人片在线观看蜜桃| 亚洲精品无码久久久久苍井空| 久久综合色天天久久综合图片| 亚洲AV伊人久久青青草原| 精品亚洲成AV人在线观看| 在线麻豆精东9制片厂AV影现网| 精品国色天香一卡2卡3卡 | 亚洲日产中文字幕无码| 免费观看电视剧全集在线播放| 超清无码熟妇人妻AV在线电影| 欧美军警GAY巨大粗长| 最新亚洲人成网站在线观看| 热爆料-热门吃瓜-黑料不打烊| 国产边做饭边被躁在线播放91| 日韩人妻精品一区二区三区视频 | 国产精品另类激情久久久免费| 亚洲国产AV玩弄放荡人妇系列| 毛片内射久久久一区| 英语老师没戴套让我C了一节课 | 日韩精品专区在线影院重磅| 国产免费无遮挡吃奶视频| 一本久道久久综合狠狠老| 青青青国产成人久久111网站| 国产精品拍天天在线| 野花影视免费高清观看| 日本打扑克啪啪超爽网站| 国产日韩一区在线精品| 曰曰摸天天添天天湿| 少妇厨房愉情理伦片BD在线观看 | JULIA无码人妻中文字幕在线| 女性の乳頭を遮住する方法| 国产AV午夜精品一区二区三区| 亚洲人成图片小说网站| 久久精品国产99国产精2020| 亚洲欲色欲色XXXXX在线| 全免费A级毛片免费看视频| 国产亚洲午夜高清国产拍精品| 中文字幕爆乳JULIA女教师| 色综合视频一区中文字幕| 久久久久国色AⅤ免费看| 成人片国产精品亚洲| 天天狠天天透天干天天怕| 国产精品国产三级国产专区50| 无码国内精品人妻少妇| 国产亚洲情侣一区二区无| 在床上拔萝卜又疼又叫什么病| 丝瓜成视频人APP下载网站| 久久人妻蜜桃一区二区三区| 国产96在线 | 亚洲| 中国自产一战二战三战来源|