redis(二:日志)

持久化對于任何數(shù)據(jù)庫來說都是重要的知識點(diǎn)食侮。很久前寫過mongo和mysql的日志。今天記錄下redis的日志設(shè)計目胡。

Redis把后端數(shù)據(jù)庫中的數(shù)據(jù)存儲在內(nèi)存中锯七,然后直接從內(nèi)存中讀取數(shù)據(jù),響應(yīng)速度會非秤海快眉尸。但服務(wù)器宕機(jī)時,內(nèi)存中的數(shù)據(jù)將全部丟失巨双。如果從后端數(shù)據(jù)庫(比如:mysql)恢復(fù)這些數(shù)據(jù)噪猾,那會出現(xiàn)大的緩存穿透,會給數(shù)據(jù)庫帶來巨大的壓力筑累;而且從慢速數(shù)據(jù)庫中讀取性能肯定比不上從 Redis 中讀取袱蜡,導(dǎo)致使用程序響應(yīng)變慢。所以慢宗,對 Redis 來說坪蚁,實(shí)現(xiàn)數(shù)據(jù)的持久化很重要。
目前镜沽,Redis 的持久化主要有兩大機(jī)制敏晤,即 AOF(Append Only File)日志和 RDB 快照。

寫后日志:AOF
Redis 是先執(zhí)行命令缅茉,把數(shù)據(jù)寫入內(nèi)存嘴脾,然后才記錄日志,如下圖所示:


傳統(tǒng)數(shù)據(jù)庫的日志蔬墩,例如 redo log(重做日志)译打,記錄的是修改后的數(shù)據(jù)耗拓,而 AOF 里記錄的是 Redis 收到的每一條命令,這些命令是以文本形式保存的扶平。
我們以 Redis 收到“set testkey testvalue”命令后記錄的日志為例帆离,看看 AOF 日志的內(nèi)容。其中结澄,“*3”表示當(dāng)前命令有三個部分哥谷,每部分都是由“$+數(shù)字”開頭,后面緊跟著具體的命令麻献、鍵或值们妥。這里,“數(shù)字”表示這部分中的命令勉吻、鍵或值一共有多少字節(jié)监婶。例如,“$3 set”表示這部分有 3 個字節(jié)齿桃,也就是“set”命令惑惶。

寫后日志的好處:
1,為了避免額外的檢查開銷短纵,Redis 在向 AOF 里面記錄日志的時候带污,并不會先去對這些命令進(jìn)行語法檢查。寫后日志這種方式保證了寫入日志的命令都是合法的香到。
2鱼冀,是在命令執(zhí)行后才記錄日志,所以不會阻塞當(dāng)前的寫操作悠就。

AOF落盤策略
Always千绪,同步寫回:每個寫命令執(zhí)行完,立馬同步地將日志寫回磁盤梗脾;
Everysec荸型,每秒寫回:每個寫命令執(zhí)行完,只是先把日志寫到 AOF 文件的內(nèi)存緩沖區(qū)炸茧,每隔一秒把緩沖區(qū)中的內(nèi)容寫入磁盤帆疟;
No,操作系統(tǒng)控制的寫回:每個寫命令執(zhí)行完宇立,只是先把日志寫到 AOF 文件的內(nèi)存緩沖區(qū),由操作系統(tǒng)決定何時將緩沖區(qū)內(nèi)容寫回磁盤自赔。

write 和 fsync
write 只要把日志記錄寫到內(nèi)核緩沖區(qū)妈嘹,就可以返回了,并不需要等待日志實(shí)際寫回到磁盤绍妨;
fsync 需要把日志記錄寫回到磁盤后才能返回润脸,時間較長柬脸。


當(dāng)寫回策略配置為 everysec 時,Redis 會使用后臺的子線程異步完成 fsync 的操作毙驯。
always 策略并不使用后臺子線程來執(zhí)行倒堕。

落盤策略性能比較
Always可以做到基本不丟數(shù)據(jù)(執(zhí)行瞬間立刻宕機(jī),還未落盤還是會丟失數(shù)據(jù))爆价,缺點(diǎn):每一個寫命令后都有一個慢速的落盤操作垦巴,不可避免地會影響主線程性能;

AOF 重寫機(jī)制

AOF 是以文件的形式在記錄接收到的所有寫命令铭段。隨著時間推移骤宣,AOF 文件會不斷膨脹。需要注意 AOF 文件過大帶來的性能問題序愚。(如果發(fā)生宕機(jī)憔披,AOF 中記錄的命令要一個個被重新執(zhí)行,用于故障恢復(fù)爸吮,如果日志文件太大芬膝,整個恢復(fù)過程就會非常緩慢),這時候就需要用到AOF 重寫機(jī)制形娇。

重寫機(jī)制通過“多變一”(舊日志文件中的多條命令锰霜,在重寫后的新日志中變成了一條命令)的方法,縮小 AOF 文件埂软。


什么時候會觸發(fā)AOF 重寫?
有兩個配置項(xiàng)在控制AOF重寫的觸發(fā)時機(jī):
1, auto-aof-rewrite-min-size: 表示運(yùn)行AOF重寫時文件的最小大小锈遥,默認(rèn)為64MB
2, auto-aof-rewrite-percentage: 這個值的計算方法是:當(dāng)前AOF文件大小和上一次重寫后AOF文件大小的差值,再除以上一次重寫后AOF文件大小勘畔。也就是當(dāng)前AOF文件比上一次重寫后AOF文件的增量大小所灸,和上一次重寫后AOF文件大小的比值。
AOF文件大小同時超出上面這兩個配置項(xiàng)時炫七,會觸發(fā)AOF重寫爬立。

AOF重寫對主線程的影響

和 AOF日志落盤不同浑劳,重寫過程是由后臺子進(jìn)程 bgrewriteaof 來完成的塘慕。這個過程并不會阻塞主線程蒋院。

重寫的過程總結(jié)為“一個拷貝坊夫,兩處日志”万皿。
一個拷貝
每次執(zhí)行重寫時化漆,主線程 fork 出后臺的 bgrewriteaof 子進(jìn)程棠绘。此時苟翻,fork 會把主線程的內(nèi)存映射拷貝一份給 bgrewriteaof 子進(jìn)程的止,這里面就包含了數(shù)據(jù)庫的最新數(shù)據(jù)檩坚。然后,bgrewriteaof 子進(jìn)程就可以在不影響主線程的情況下,逐一把拷貝的數(shù)據(jù)寫成操作匾委,記入重寫日志拖叙。
兩處日志
舊的AOF日志:因?yàn)橹骶€程未阻塞,仍然可以處理新來的操作赂乐。Redis 會把這個操作寫到它的緩沖區(qū)(很快就合到AOF日志中薯鳍,需要看落盤策略)。這樣一來挨措,即使宕機(jī)了挖滤,這個 AOF 日志的操作仍然是齊全的,可以用于恢復(fù)运嗜。
新的AOF日志:AOF 重寫日志壶辜。這個操作也會被寫到重寫日志的緩沖區(qū)。(拷貝完才合到日志中)這樣担租,重寫日志也不會丟失最新的操作砸民。等到拷貝數(shù)據(jù)的所有操作記錄重寫完成后,重寫日志記錄的這些最新操作也會寫入新的 AOF 文件奋救,以保證數(shù)據(jù)庫最新狀態(tài)的記錄岭参。此時,我們就可以用新的 AOF 文件替代舊文件了尝艘。

aof緩沖區(qū):是正常使用aof作為數(shù)據(jù)落地中間地帶演侯,所有的數(shù)據(jù)先到aof緩沖區(qū)再到aof文件中。
aof重寫緩沖區(qū): 是aof重寫時背亥,redis還要繼續(xù)接收數(shù)據(jù)秒际,這個數(shù)據(jù)就寫到aof重寫緩沖區(qū),當(dāng)aof重寫ok時狡汉,主進(jìn)程在把a(bǔ)of重寫緩沖區(qū)的數(shù)據(jù)寫到aof緩沖區(qū)娄徊,最后fsync到aof文件中。

AOF重寫的時候盾戴,子線程會首先拷貝必要的數(shù)據(jù)結(jié)構(gòu)包括內(nèi)存頁表寄锐,完成了這個操作就可以進(jìn)行重寫,只不過父子進(jìn)程這個時候指向的是同一個內(nèi)存尖啡,在子進(jìn)程重寫過程中若父進(jìn)程操作了已有的key橄仆,則會重新申請新的內(nèi)存,這樣父子進(jìn)程就逐漸的擁有獨(dú)自的內(nèi)存空間衅斩。
總結(jié)Linux的Copy On Write技術(shù):
1盆顾,fork出的子進(jìn)程共享父進(jìn)程的物理空間,當(dāng)父子進(jìn)程有內(nèi)存寫入操作時畏梆,read-only內(nèi)存頁發(fā)生中斷椎扬,將觸發(fā)的異常的內(nèi)存頁復(fù)制一份(其余的頁還是共享父進(jìn)程的)惫搏。
2,fork出的子進(jìn)程功能實(shí)現(xiàn)和父進(jìn)程是一樣的蚕涤。如果有需要,我們會用exec()把當(dāng)前進(jìn)程映像替換成新的進(jìn)程文件铣猩,完成自己想要實(shí)現(xiàn)的功能揖铜。
Copy On Write機(jī)制了解一下

簡單來說就是重寫是復(fù)制一份地址映射,父線程只要改動了达皿,子線程就開辟新的空間天吓,映射修改。然后還記錄一份緩沖區(qū)緩存日志峦椰,等備份完再執(zhí)行一下緩沖區(qū)的日志龄寞。

Redis采用fork子進(jìn)程重寫AOF文件時,潛在的阻塞風(fēng)險包括:fork子進(jìn)程 和 AOF重寫過程中父進(jìn)程產(chǎn)生寫入的場景
fork子進(jìn)程汤功,fork這個瞬間一定是會阻塞主線程的物邑,fork采用操作系統(tǒng)提供的寫實(shí)復(fù)制(Copy On Write)機(jī)制,就是為了避免一次性拷貝大量內(nèi)存數(shù)據(jù)給子進(jìn)程造成的長時間阻塞問題滔金,但fork子進(jìn)程需要拷貝進(jìn)程必要的數(shù)據(jù)結(jié)構(gòu)色解,其中有一項(xiàng)就是拷貝內(nèi)存頁表(虛擬內(nèi)存和物理內(nèi)存的映射索引表),這個拷貝過程會消耗大量CPU資源餐茵,拷貝完成之前整個進(jìn)程是會阻塞的科阎,阻塞時間取決于整個實(shí)例的內(nèi)存大小,實(shí)例越大忿族,內(nèi)存頁表越大锣笨,fork阻塞時間越久。
拷貝內(nèi)存頁表完成后道批,子進(jìn)程與父進(jìn)程指向相同的內(nèi)存地址空間错英,也就是說此時雖然產(chǎn)生了子進(jìn)程,但是并沒有申請與父進(jìn)程相同的內(nèi)存大小屹徘。那什么時候父子進(jìn)程才會真正內(nèi)存分離呢走趋?“寫實(shí)復(fù)制”顧名思義,就是在寫發(fā)生時噪伊,才真正拷貝內(nèi)存真正的數(shù)據(jù)簿煌,這個過程中,父進(jìn)程也可能會產(chǎn)生阻塞的風(fēng)險鉴吹,就是下面介紹的場景姨伟。

fork出的子進(jìn)程指向與父進(jìn)程相同的內(nèi)存地址空間,此時子進(jìn)程就可以執(zhí)行AOF重寫豆励,把內(nèi)存中的所有數(shù)據(jù)寫入到AOF文件中夺荒。但是此時父進(jìn)程依舊是會有流量寫入的瞒渠,如果父進(jìn)程操作的是一個已經(jīng)存在的key,那么這個時候父進(jìn)程就會真正拷貝這個key對應(yīng)的內(nèi)存數(shù)據(jù)技扼,申請新的內(nèi)存空間伍玖,這樣逐漸地,父子進(jìn)程內(nèi)存數(shù)據(jù)開始分離剿吻,父子進(jìn)程逐漸擁有各自獨(dú)立的內(nèi)存空間窍箍。因?yàn)閮?nèi)存分配是以頁為單位進(jìn)行分配的,默認(rèn)4k丽旅,如果父進(jìn)程此時操作的是一個bigkey椰棘,重新申請大塊內(nèi)存耗時會變長,可能會產(chǎn)阻塞風(fēng)險榄笙。另外邪狞,如果操作系統(tǒng)開啟了內(nèi)存大頁機(jī)制(Huge Page,頁面大小2M)茅撞,那么父進(jìn)程申請內(nèi)存時阻塞的概率將會大大提高帆卓,所以在Redis機(jī)器上需要關(guān)閉Huge Page機(jī)制。Redis每次fork生成RDB或AOF重寫完成后乡翅,都可以在Redis log中看到父進(jìn)程重新申請了多大的內(nèi)存空間鳞疲。

當(dāng)主線程使用后臺子線程執(zhí)行了一次 fsync,需要再次把新接收的操作記錄寫回磁盤時蠕蚜,如果主線程發(fā)現(xiàn)上一次的 fsync 還沒有執(zhí)行完尚洽,那么它就會阻塞。所以靶累,如果后臺子線程執(zhí)行的 fsync 頻繁阻塞的話(比如 AOF 重寫占用了大量的磁盤 IO 帶寬)腺毫,主線程也會阻塞,導(dǎo)致 Redis 性能變慢挣柬。


如果業(yè)務(wù)應(yīng)用對延遲非常敏感潮酒,但同時允許一定量的數(shù)據(jù)丟失,那么邪蛔,可以把配置項(xiàng) no-appendfsync-on-rewrite 設(shè)置為 yes急黎。這個配置項(xiàng)設(shè)置為 yes 時,表示在 AOF 重寫時侧到,不進(jìn)行 fsync 操作勃教。也就是說,Redis 實(shí)例把寫命令寫到內(nèi)存后匠抗,不調(diào)用后臺線程進(jìn)行 fsync 操作故源,就可以直接返回了。

落盤時機(jī)和重寫機(jī)制都是在“記日志”這一過程中發(fā)揮作用的汞贸。例如绳军,落盤時機(jī)的選擇可以避免記日志時阻塞主線程印机,重寫可以避免日志文件過大。但是门驾,在“用日志”的過程中射赛,也就是使用 AOF 進(jìn)行故障恢復(fù)時,我們?nèi)匀恍枰阉械牟僮饔涗浂歼\(yùn)行一遍奶是。再加上 Redis 的單線程設(shè)計咒劲,這些命令操作只能一條一條按順序執(zhí)行,這個“重放”的過程就會很慢了诫隅。
有沒有既能避免數(shù)據(jù)丟失,又能更快地恢復(fù)的方法呢帐偎?當(dāng)然有逐纬,那就是 RDB 快照了。

摘抄:《Redis 核心技術(shù)與實(shí)戰(zhàn)》-第4節(jié)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末削樊,一起剝皮案震驚了整個濱河市豁生,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌漫贞,老刑警劉巖甸箱,帶你破解...
    沈念sama閱讀 218,525評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異迅脐,居然都是意外死亡芍殖,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,203評論 3 395
  • 文/潘曉璐 我一進(jìn)店門谴蔑,熙熙樓的掌柜王于貴愁眉苦臉地迎上來豌骏,“玉大人,你說我怎么就攤上這事隐锭∏远悖” “怎么了?”我有些...
    開封第一講書人閱讀 164,862評論 0 354
  • 文/不壞的土叔 我叫張陵钦睡,是天一觀的道長蒂窒。 經(jīng)常有香客問我,道長荞怒,這世上最難降的妖魔是什么洒琢? 我笑而不...
    開封第一講書人閱讀 58,728評論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮挣输,結(jié)果婚禮上纬凤,老公的妹妹穿的比我還像新娘。我一直安慰自己撩嚼,他們只是感情好停士,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,743評論 6 392
  • 文/花漫 我一把揭開白布挖帘。 她就那樣靜靜地躺著,像睡著了一般恋技。 火紅的嫁衣襯著肌膚如雪拇舀。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,590評論 1 305
  • 那天蜻底,我揣著相機(jī)與錄音骄崩,去河邊找鬼。 笑死薄辅,一個胖子當(dāng)著我的面吹牛要拂,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播站楚,決...
    沈念sama閱讀 40,330評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼脱惰,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了窿春?” 一聲冷哼從身側(cè)響起拉一,我...
    開封第一講書人閱讀 39,244評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎旧乞,沒想到半個月后蔚润,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,693評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡尺栖,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,885評論 3 336
  • 正文 我和宋清朗相戀三年嫡纠,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片决瞳。...
    茶點(diǎn)故事閱讀 40,001評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡货徙,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出皮胡,到底是詐尸還是另有隱情痴颊,我是刑警寧澤,帶...
    沈念sama閱讀 35,723評論 5 346
  • 正文 年R本政府宣布屡贺,位于F島的核電站蠢棱,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏甩栈。R本人自食惡果不足惜泻仙,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,343評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望量没。 院中可真熱鬧玉转,春花似錦、人聲如沸殴蹄。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,919評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至刺下,卻和暖如春绑嘹,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背橘茉。 一陣腳步聲響...
    開封第一講書人閱讀 33,042評論 1 270
  • 我被黑心中介騙來泰國打工工腋, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人畅卓。 一個月前我還...
    沈念sama閱讀 48,191評論 3 370
  • 正文 我出身青樓擅腰,卻偏偏與公主長得像,于是被迫代替她去往敵國和親翁潘。 傳聞我的和親對象是個殘疾皇子惕鼓,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,955評論 2 355

推薦閱讀更多精彩內(nèi)容