在Redis的使用當(dāng)中燃领,持久化一直是一個(gè)比較重要的話(huà)題躺苦,很多同學(xué)在使用Redis的過(guò)程中對(duì)持久化策略如何選擇直奋、如何配置持久化存在疑問(wèn)检疫。本文試圖對(duì)Redis的持久化做比較系統(tǒng)地分析比較,以期達(dá)到能夠正確理解Redis的持久化鸵贬,并且能夠結(jié)合應(yīng)用實(shí)際選擇合理的持久化機(jī)制的目的俗他。
1. 背景知識(shí)
持久化是一個(gè)數(shù)據(jù)存儲(chǔ)世界中的普遍話(huà)題。持久化可以描述為將關(guān)心的數(shù)據(jù)存儲(chǔ)在非易失性存儲(chǔ)(non-volatile memory)的過(guò)程阔逼。當(dāng)數(shù)據(jù)得到持久化后兆衅,即使發(fā)生一定程度的故障,只要持久化設(shè)備不損壞嗜浮,我們都可以利用持久化的數(shù)據(jù)進(jìn)行恢復(fù)羡亩,在一定程度上降低故障帶來(lái)的損失。
我們使用的數(shù)據(jù)存儲(chǔ)危融,不論是傳統(tǒng)的關(guān)系型數(shù)據(jù)庫(kù)還是類(lèi)似Redis的所謂NoSQL畏铆,當(dāng)他們運(yùn)行起來(lái)之后,都是一個(gè)運(yùn)行于操作系統(tǒng)之上的程序吉殃,由應(yīng)用程序發(fā)送的數(shù)據(jù)都需要由這些數(shù)據(jù)存儲(chǔ)程序先進(jìn)行一定的處理及志,然后才能按照一定的格式進(jìn)行持久化存儲(chǔ)片排。這個(gè)過(guò)程中數(shù)據(jù)必然先經(jīng)過(guò)應(yīng)用程序的內(nèi)存寨腔,由CPU進(jìn)行一定的運(yùn)算速侈,然后才能存儲(chǔ)到持久化設(shè)備上。所以要理解持久化迫卢,就先得具備幾個(gè)方面的背景知識(shí)倚搬。
1.1 用戶(hù)空間、內(nèi)核空間以及內(nèi)核緩沖區(qū)
在Linux中乾蛤,為了保證操作系統(tǒng)穩(wěn)定每界、高效運(yùn)行,內(nèi)存空間被劃分為用戶(hù)空間和內(nèi)核空間家卖。簡(jiǎn)單來(lái)說(shuō)眨层,各個(gè)應(yīng)用程序一般情況下位于用戶(hù)空間,而操作系統(tǒng)內(nèi)核運(yùn)行于內(nèi)核空間上荡。之所以要做如此劃分趴樱,是因?yàn)閷?duì)操作系統(tǒng)很多函數(shù)的調(diào)用,都會(huì)觸發(fā)敏感資源的操作酪捡,例如清理內(nèi)存叁征、設(shè)置時(shí)鐘等等。為了防止各個(gè)程序操作敏感資源造成系統(tǒng)崩潰逛薇,將內(nèi)存空間進(jìn)行了上述劃分捺疼。在用戶(hù)空間內(nèi),應(yīng)用程序并不具備對(duì)敏感資源操作的權(quán)限(相應(yīng)的指令被限制執(zhí)行)永罚,這樣就避免了可能發(fā)生的系統(tǒng)崩潰等等異称『簦現(xiàn)象。
我們知道呢袱,絕大部分應(yīng)用程序都避免不了和底層資源打交道官扣,例如從磁盤(pán)讀取數(shù)據(jù)、從網(wǎng)絡(luò)讀取或者接收?qǐng)?bào)文等产捞。但是醇锚,劃分了用戶(hù)空間和系統(tǒng)空間后,應(yīng)用程序沒(méi)有權(quán)限訪(fǎng)問(wèn)系統(tǒng)資源坯临。為了解決這個(gè)問(wèn)題焊唬,操作系統(tǒng)暴露了一系列系統(tǒng)調(diào)用接口(System Call Interface)供應(yīng)用程序和底層資源交互,這樣應(yīng)用程序可以通過(guò)調(diào)用這些接口實(shí)現(xiàn)資源的訪(fǎng)問(wèn)看靠。我們常見(jiàn)的系統(tǒng)調(diào)用有write(),read()等等疗隶。
那么當(dāng)應(yīng)用程序觸發(fā)了系統(tǒng)調(diào)用接口之后會(huì)發(fā)生什么呢词顾。我們以寫(xiě)入某個(gè)文件為例,當(dāng)應(yīng)用程序調(diào)用了寫(xiě)入操作,系統(tǒng)并不會(huì)直接訪(fǎng)問(wèn)硬盤(pán)進(jìn)行文件寫(xiě)入,而是首先將文件寫(xiě)入內(nèi)核緩沖區(qū)科盛,此后再定時(shí)批量將內(nèi)核緩沖區(qū)中的數(shù)據(jù)寫(xiě)入磁盤(pán)(這個(gè)過(guò)程也可以由應(yīng)用程序通過(guò)觸發(fā)調(diào)用fsync()來(lái)完成)。非常明顯,之所以會(huì)劃分出內(nèi)核緩沖區(qū)老速,主要是為了解決底層IO讀寫(xiě)速度和內(nèi)存讀寫(xiě)速度的不匹配,如果頻繁地同步寫(xiě)入硬盤(pán)凸主,將會(huì)嚴(yán)重拖慢程序的運(yùn)行速度橘券。
總的來(lái)說(shuō),對(duì)于理解持久化的過(guò)程卿吐,我們需要知道:在Linux操作系統(tǒng)中旁舰,內(nèi)存被劃分為用戶(hù)空間和內(nèi)核空間,當(dāng)應(yīng)用程序中的數(shù)據(jù)需要寫(xiě)入文件時(shí)嗡官,會(huì)依次經(jīng)過(guò)應(yīng)用程序內(nèi)存箭窜、內(nèi)核緩沖區(qū)最終寫(xiě)入硬盤(pán)中,這個(gè)過(guò)程中涉及到的系統(tǒng)調(diào)用有寫(xiě)入操作write()和強(qiáng)制硬盤(pán)同步操作fsync()衍腥。
1.2 寫(xiě)時(shí)復(fù)制Copy-on-Write
我們知道磺樱,在linux系統(tǒng)中,可以通過(guò)fork()調(diào)用創(chuàng)建出一個(gè)與父進(jìn)程完全相同的子進(jìn)程拷貝紧阔。但是坊罢,在fork的過(guò)程中,如果父進(jìn)程所占用的內(nèi)存空間過(guò)大擅耽,單單完成內(nèi)存數(shù)據(jù)的拷貝活孩,耗時(shí)可能就會(huì)很久,這樣會(huì)阻塞父進(jìn)程響應(yīng)其他命令乖仇,這對(duì)一個(gè)面向客戶(hù)的服務(wù)端程序來(lái)說(shuō)憾儒,是不能接受的。
為了解決這個(gè)問(wèn)題乃沙,大佬們提出了寫(xiě)時(shí)復(fù)制技術(shù)起趾。簡(jiǎn)單來(lái)說(shuō),寫(xiě)時(shí)復(fù)制技術(shù)是指警儒,在fork的過(guò)程中训裆,對(duì)應(yīng)用程序的內(nèi)存不進(jìn)行整個(gè)拷貝,而是在子進(jìn)程中創(chuàng)建一個(gè)指向父進(jìn)程對(duì)應(yīng)內(nèi)存地址的引用蜀铲,只有當(dāng)子進(jìn)程讀取內(nèi)存并且發(fā)現(xiàn)數(shù)據(jù)在拷貝之后發(fā)生了新的寫(xiě)入時(shí)边琉,才進(jìn)行實(shí)際的拷貝動(dòng)作。這樣记劝,由于在fork的過(guò)程中不需要完成整個(gè)數(shù)據(jù)的拷貝变姨,大大降低了fork()調(diào)用的耗時(shí)。
但實(shí)際上厌丑,寫(xiě)時(shí)復(fù)制技術(shù)并沒(méi)有完全解決問(wèn)題定欧。比如渔呵,某些大型應(yīng)用程序,占用的內(nèi)存空間非常大(例如一個(gè)占用幾十G內(nèi)存的Redis實(shí)例)砍鸠,僅僅完成內(nèi)存地址的指向就會(huì)耗時(shí)很久扩氢。
最后,對(duì)于Redis持久化來(lái)說(shuō)睦番,我們只需要知道类茂,在持久化的過(guò)程中會(huì)涉及fork調(diào)用(RDB方式和AOF重寫(xiě)時(shí)發(fā)生),雖然Linux采用了寫(xiě)時(shí)復(fù)制技術(shù)托嚣,在實(shí)例內(nèi)存占用較大時(shí),fork調(diào)用仍舊可能帶來(lái)長(zhǎng)時(shí)間的阻塞厚骗。
前面我們簡(jiǎn)要討論了Linux的內(nèi)存空間劃分以及寫(xiě)時(shí)復(fù)制(Linux對(duì)fork調(diào)用的消耗做出的一種優(yōu)化示启,即使進(jìn)行了這種優(yōu)化,高內(nèi)存占用的實(shí)例在fork時(shí)仍可能長(zhǎng)時(shí)間阻塞)這兩個(gè)背景知識(shí)點(diǎn)领舰。下面我們對(duì)持久化過(guò)程中數(shù)據(jù)的寫(xiě)入過(guò)程進(jìn)行分析夫嗓。
2. 持久化數(shù)據(jù)寫(xiě)入的過(guò)程
當(dāng)我們采用某種數(shù)據(jù)存儲(chǔ)保存應(yīng)用數(shù)據(jù)的時(shí)候,數(shù)據(jù)由應(yīng)用程序通過(guò)網(wǎng)絡(luò)發(fā)送至數(shù)據(jù)存儲(chǔ)程序冲秽,再由數(shù)據(jù)存儲(chǔ)程序進(jìn)行加工計(jì)算舍咖,然后以一定的格式存儲(chǔ)在硬盤(pán)等持久化設(shè)備中。這個(gè)過(guò)程涉及到網(wǎng)絡(luò)調(diào)用锉桑、程序加工排霉、操作系統(tǒng)寫(xiě)入文件等多個(gè)步驟,為了更清晰地進(jìn)行接下來(lái)的討論民轴,我們把這個(gè)步驟進(jìn)行一定的分解攻柠。簡(jiǎn)要來(lái)說(shuō),應(yīng)用數(shù)據(jù)從產(chǎn)生到被存儲(chǔ)到持久化設(shè)備后裸,經(jīng)過(guò)了如下步驟:
客戶(hù)端向數(shù)據(jù)庫(kù)服務(wù)端發(fā)送寫(xiě)入或者更新數(shù)據(jù)的請(qǐng)求瑰钮,此時(shí)數(shù)據(jù)位于客戶(hù)端內(nèi)存中
服務(wù)端接收到寫(xiě)命令,此時(shí)數(shù)據(jù)位于服務(wù)端數(shù)據(jù)庫(kù)應(yīng)用內(nèi)存中(站在服務(wù)端服務(wù)器視角微驶,數(shù)據(jù)位于應(yīng)用(數(shù)據(jù)庫(kù))內(nèi)存中浪谴,即用戶(hù)態(tài)內(nèi)存)
數(shù)據(jù)庫(kù)調(diào)用系統(tǒng)函數(shù)向硬盤(pán)寫(xiě)數(shù)據(jù),此時(shí)數(shù)據(jù)位于內(nèi)核緩沖區(qū)中(kernel’s buffer)
操作系統(tǒng)將數(shù)據(jù)從寫(xiě)緩沖區(qū)轉(zhuǎn)移到硬盤(pán)控制器因苹,此時(shí)數(shù)據(jù)位于硬盤(pán)緩沖區(qū)(disk cache)
硬盤(pán)控制器將數(shù)據(jù)實(shí)際寫(xiě)入物理介質(zhì)中
通常步驟②的實(shí)現(xiàn)因數(shù)據(jù)庫(kù)的實(shí)現(xiàn)不同而不同苟耻,但相同的是,最終都會(huì)觸發(fā)調(diào)用系統(tǒng)寫(xiě)函數(shù)進(jìn)行數(shù)據(jù)寫(xiě)入容燕。步驟③也因不同系統(tǒng)的實(shí)現(xiàn)而不同梁呈,但在討論當(dāng)前問(wèn)題時(shí),可將其視為單獨(dú)的一步而不用關(guān)心其細(xì)節(jié)蘸秘。
通過(guò)上述分解的步驟我們看到官卡,數(shù)據(jù)先后經(jīng)過(guò)了客戶(hù)端(應(yīng)用程序)內(nèi)存蝗茁、服務(wù)端(數(shù)據(jù)存儲(chǔ)程序)內(nèi)存、內(nèi)核緩沖區(qū)寻咒、硬盤(pán)緩沖區(qū)哮翘,最終被寫(xiě)入物理介質(zhì)中。
3. 衡量持久化的標(biāo)準(zhǔn)
前面我們對(duì)持久化涉及的數(shù)據(jù)流轉(zhuǎn)過(guò)程進(jìn)行了分步討論毛秘。下面我們主要從更加普遍的意義上討論持久化饭寺,主要包括持久化的目的以及如何衡量某應(yīng)用的持久化能力是否優(yōu)秀。
3.1 靈魂發(fā)問(wèn):為什么要做持久化
要理解一個(gè)事物叫挟,我們必須得理解這個(gè)事物出現(xiàn)的原因艰匙。
有一定開(kāi)發(fā)經(jīng)驗(yàn)的同學(xué)都知道,在計(jì)算機(jī)世界中抹恳,意外無(wú)處不在员凝,除了由于各種原因產(chǎn)生的軟件Bug,硬件設(shè)備奋献,例如內(nèi)存健霹、網(wǎng)絡(luò)、磁盤(pán)等等都可能發(fā)生故障瓶蚂。更有甚者糖埋,對(duì)于一些重要性比較高的應(yīng)用來(lái)說(shuō),可能還不得不考慮外部世界的干擾窃这,例如:由于機(jī)房空調(diào)故障導(dǎo)致的服務(wù)器停止運(yùn)行瞳别,市政施工導(dǎo)致的網(wǎng)絡(luò)線(xiàn)纜被挖斷等等。我們?cè)跇?gòu)建應(yīng)用系統(tǒng)時(shí)钦听,不得不考慮這些各種各樣的異常情況洒试,來(lái)提高我們系統(tǒng)的可靠性(非功能)。對(duì)于數(shù)據(jù)存儲(chǔ)系統(tǒng)來(lái)說(shuō)朴上,數(shù)據(jù)是其核心資產(chǎn)垒棋,如果因?yàn)橐恍┹p微的故障導(dǎo)致了數(shù)據(jù)的丟失,那么這個(gè)數(shù)據(jù)存儲(chǔ)系統(tǒng)不僅不是可靠的痪宰,甚至可以說(shuō)是不可用的叼架。
進(jìn)行持久化,可以理解為提高數(shù)據(jù)存儲(chǔ)系統(tǒng)可靠性的一種技術(shù)手段衣撬,只有當(dāng)將數(shù)據(jù)存儲(chǔ)到非易失性存儲(chǔ)設(shè)備上后乖订,才能保證在一定程度的故障面前(自然災(zāi)害面前,人類(lèi)創(chuàng)造的事物往往不堪一擊)具练,數(shù)據(jù)不會(huì)丟失乍构。
那么,對(duì)于不同的數(shù)據(jù)存儲(chǔ)系統(tǒng)扛点,他們都實(shí)現(xiàn)了持久化哥遮,我們?cè)趺春饬克麄儗?shí)現(xiàn)的持久化到底好不好呢岂丘?前面已經(jīng)說(shuō)過(guò),持久化主要解決由于各種故障引起數(shù)據(jù)丟失的問(wèn)題眠饮,那么奥帘,我們?cè)诳紤]某種持久化機(jī)制的時(shí)候,可以從兩個(gè)維度對(duì)其進(jìn)行考量:
故障發(fā)生后仪召,我的數(shù)據(jù)會(huì)不會(huì)丟寨蹋,會(huì)丟失多少。
故障發(fā)生后扔茅,我是不是可以利用持久化的數(shù)據(jù)進(jìn)行數(shù)據(jù)恢復(fù)已旧,這種恢復(fù)的成本和效率如何。
這可以通過(guò)如下兩個(gè)指標(biāo)來(lái)衡量咖摹。
3.2 Durability(持久性)
故障發(fā)生后评姨,我們的數(shù)據(jù)是否得到了正確的保存,從而能夠在系統(tǒng)恢復(fù)時(shí)得到恢復(fù)萤晴,這是我們關(guān)心的首要問(wèn)題。首先要考慮的問(wèn)題是胁后,可能發(fā)生哪些故障店读。不考慮機(jī)房被卡車(chē)撞了或者電纜被鯊魚(yú)咬斷這種意外事件,我們可以對(duì)可能發(fā)生的異常進(jìn)行如下劃分:
1. 應(yīng)用級(jí)的異常攀芯。這種異常是由于諸如數(shù)據(jù)庫(kù)服務(wù)被異常關(guān)閉屯断,如kill -9等。這時(shí)服務(wù)器仍舊正常運(yùn)轉(zhuǎn)侣诺。那么這時(shí)當(dāng)上述討論的第③步完成時(shí)殖演,可以認(rèn)為數(shù)據(jù)是安全的。因?yàn)槟暝В词勾撕髷?shù)據(jù)庫(kù)應(yīng)用被異常關(guān)閉趴久,數(shù)據(jù)仍舊會(huì)被寫(xiě)入物理介質(zhì)中,此后的操作已可以由操作系統(tǒng)獨(dú)立完成搔确。
2. 服務(wù)器級(jí)異常彼棍,例如掉電。這種情況下膳算,只有當(dāng)?shù)冖莶酵瓿蓵r(shí)座硕,才可以認(rèn)為數(shù)據(jù)是真正安全的。
可見(jiàn)涕蜂,對(duì)于持久化來(lái)說(shuō)华匾,關(guān)鍵的是上述③、④机隙、⑤三個(gè)步驟的執(zhí)行情況蜘拉,從另一個(gè)角度考慮三個(gè)步驟的動(dòng)作萨西,他們分別表示的含義分別是:
數(shù)據(jù)從用戶(hù)態(tài)內(nèi)存向系統(tǒng)態(tài)內(nèi)存的轉(zhuǎn)移頻率(write()操作的調(diào)用頻率)
系統(tǒng)多久將數(shù)據(jù)從系統(tǒng)態(tài)內(nèi)存轉(zhuǎn)移到硬盤(pán)控制器中
硬盤(pán)控制器多久將數(shù)據(jù)寫(xiě)入物理介質(zhì)中
在第三步中,數(shù)據(jù)存儲(chǔ)程序可以控制通過(guò)調(diào)用系統(tǒng)函數(shù)write頻率诸尽,但是調(diào)用該函數(shù)消耗的時(shí)間卻無(wú)法得到控制原杂。因?yàn)閣rite函數(shù)的成功返回,依賴(lài)于寫(xiě)入數(shù)據(jù)量的大小及硬盤(pán)的實(shí)際寫(xiě)入能力您机,當(dāng)硬盤(pán)無(wú)法實(shí)際處理寫(xiě)入請(qǐng)求時(shí)穿肄,數(shù)據(jù)會(huì)被緩存到寫(xiě)入緩存中,如果進(jìn)一步緩存被寫(xiě)滿(mǎn)际看,此時(shí)write調(diào)用將會(huì)阻塞咸产,直至可以完成全部數(shù)據(jù)的寫(xiě)入時(shí),write調(diào)用才會(huì)成功返回仲闽。
在第四步中脑溢,數(shù)據(jù)的轉(zhuǎn)移由硬盤(pán)控制器控制,通常該寫(xiě)入頻率不會(huì)太高赖欣,因?yàn)榇罅克槠瑪?shù)據(jù)的寫(xiě)入相比一次寫(xiě)入大數(shù)據(jù)量更慢屑彻。在Linux的默認(rèn)實(shí)現(xiàn)中,寫(xiě)入間隔是30s顶吮。這意味著社牲,當(dāng)這一步失敗時(shí),最多可能有30s內(nèi)的數(shù)據(jù)無(wú)法持久化到硬盤(pán)中悴了。而在實(shí)際中搏恤,可以調(diào)用系統(tǒng)函數(shù)fsync()強(qiáng)制執(zhí)行該步驟。同樣地湃交,該系統(tǒng)調(diào)用在無(wú)法成功完成時(shí)熟空,也會(huì)阻塞用戶(hù)進(jìn)程,同時(shí)也會(huì)阻塞對(duì)當(dāng)前文件執(zhí)行寫(xiě)入操作的其他進(jìn)程搞莺。
第五步的實(shí)現(xiàn)應(yīng)用層面已經(jīng)無(wú)法控制息罗,這里我們不加討論。
綜上討論腮敌,我們提取要點(diǎn)阱当,關(guān)于Durability(持久性)能力的討論歸結(jié)為如下兩個(gè)問(wèn)題:
應(yīng)用級(jí),由通過(guò)write()系統(tǒng)調(diào)用保證糜工。
系統(tǒng)級(jí)弊添,由通過(guò)fsync()系統(tǒng)調(diào)用保證。
3.3 可用性
以上捌木,對(duì)Durability進(jìn)行了討論油坝。除此之外,我們還關(guān)注持久化數(shù)據(jù)的可用性,即當(dāng)發(fā)生異常時(shí)澈圈,持久化數(shù)據(jù)是否可以用來(lái)恢復(fù)現(xiàn)場(chǎng)彬檀。這里有三種可能:
數(shù)據(jù)結(jié)構(gòu)被損壞,不能恢復(fù);
損壞的數(shù)據(jù)可以通過(guò)一定的工具得到修復(fù)瞬女;
數(shù)據(jù)可用窍帝,直接加載即可。
現(xiàn)有的數(shù)據(jù)存儲(chǔ)實(shí)現(xiàn)诽偷,提供如下幾類(lèi)數(shù)據(jù)可用性保證:
當(dāng)某節(jié)點(diǎn)發(fā)生異常時(shí)坤学,數(shù)據(jù)可以通過(guò)副本(Replica)恢復(fù),因而持久化數(shù)據(jù)是否可用無(wú)關(guān)緊要报慕。
數(shù)據(jù)的持久化通過(guò)類(lèi)似日志的方式實(shí)現(xiàn)(比如mysql的binlog)
數(shù)據(jù)的持久化通過(guò)追加模式的文件實(shí)現(xiàn)深浮,這種情況下,如果對(duì)文件的寫(xiě)入是保證命令級(jí)原子性的眠冈,則也可以不用考慮數(shù)據(jù)損壞的情況飞苇。除非是在第5步寫(xiě)入時(shí)發(fā)生了系統(tǒng)異常。
小結(jié)一下蜗顽,上面我們對(duì)持久化的目的-通過(guò)將數(shù)據(jù)保存到非易失性存儲(chǔ)設(shè)備來(lái)提高系統(tǒng)的可靠性布卡、持久化的衡量指標(biāo)-Durability(數(shù)據(jù)會(huì)不會(huì)丟、可能會(huì)丟多少)和可用性(用持久化數(shù)據(jù)進(jìn)行數(shù)據(jù)恢復(fù)是否麻煩)進(jìn)行了討論雇盖。
4. Redis兩種持久化模式及其比較
眾所周知羽利,Redis提供了兩種持久化方式:內(nèi)存快照方式(RDB)以及追加寫(xiě)文件方式(AOF: append only file)。
4.1 RDB
RDB持久化的相關(guān)配置相對(duì)比較簡(jiǎn)單刊懈,主要通過(guò)save N(seconds) M(operations)的格式來(lái)實(shí)現(xiàn),該配置表示當(dāng)在N秒內(nèi)娃闲,若至少發(fā)生了M次寫(xiě)入操作虚汛,則進(jìn)行持久化。在實(shí)際當(dāng)中可以配置多個(gè)觸發(fā)條件皇帮,當(dāng)任意一個(gè)觸發(fā)條件滿(mǎn)足時(shí)卷哩,都會(huì)觸發(fā)持久化操作。
RDB采用內(nèi)存快照的方式完成持久化属拾,當(dāng)達(dá)到觸發(fā)條件后将谊,Redis將當(dāng)前的內(nèi)存全部數(shù)據(jù)以一定的格式保存為快照文件。這種快照文件是經(jīng)過(guò)壓縮的渐白,其格式非常緊湊尊浓,適合用來(lái)進(jìn)行數(shù)據(jù)備份(例如結(jié)合crontab等進(jìn)行定期的數(shù)據(jù)備份)。由于是一種格式緊湊的內(nèi)存快照文件纯衍,當(dāng)采用RDB進(jìn)行數(shù)據(jù)恢復(fù)時(shí)栋齿,其效率相比于采用AOF文件更高。
RDB的實(shí)現(xiàn)過(guò)程可以簡(jiǎn)要概括如下:首先,主進(jìn)程通過(guò)fork創(chuàng)建一個(gè)子進(jìn)程瓦堵;然后基协,子進(jìn)程將數(shù)據(jù)寫(xiě)入一個(gè)臨時(shí)的RDB文件;最后菇用,當(dāng)臨時(shí)文件完成寫(xiě)入后澜驮,通過(guò)原子操作用臨時(shí)文件替換老的RDB文件。這里需要注意的是惋鸥,很多人認(rèn)為整個(gè)持久化過(guò)程會(huì)阻塞Redis對(duì)客戶(hù)端命令的處理杂穷,事實(shí)上在RDB持久化的過(guò)程中僅有在主進(jìn)程fork子進(jìn)程的過(guò)程中可能造成對(duì)客戶(hù)端讀寫(xiě)的阻塞(尤其是當(dāng)內(nèi)存占用較高時(shí))。
最后揩慕,在性能影響方面亭畜,RDB的配置通常是數(shù)十秒甚至分鐘級(jí)的,因此采用RDB時(shí)對(duì)Redis性能的影響相比AOF更小迎卤。
4.2 AOF
AOF采用追加寫(xiě)文件的方式進(jìn)行持久化拴鸵,文件內(nèi)容是每個(gè)寫(xiě)操作包含的命令和數(shù)據(jù)內(nèi)容,具有較高的可讀性蜗搔。要打開(kāi)AOF持久化劲藐,通過(guò)如下配置實(shí)現(xiàn):
# 打開(kāi)AOF持久化
appendonly yes
AOF的完成依賴(lài)fsync調(diào)用(第2節(jié),第四步驟)樟凄,而由于fsync調(diào)用會(huì)阻塞write調(diào)用聘芜,因此fsync的調(diào)用頻率高低直接影響Redis的性能表現(xiàn),這里只能在持久性和性能間進(jìn)行取舍缝龄√郑可以通過(guò)如下配置設(shè)置fsync調(diào)用的頻率:
# fsync調(diào)用的頻率
appendfsync everysec
Redis提供了no/everysec/always三個(gè)配置項(xiàng),分別表示任何寫(xiě)入操作都觸發(fā)叔壤、每秒觸發(fā)瞎饲、不顯示觸發(fā)fsync(由操作系統(tǒng)完成,在Linux下炼绘,這個(gè)時(shí)間間隔通常為30s)嗅战,默認(rèn)的配置為everysec。這三種配置依次提供了更高的數(shù)據(jù)持久化保證俺亮,同時(shí)也帶來(lái)了更加明顯的性能影響驮捍。
由于采用追加寫(xiě)的方式,隨著寫(xiě)入操作的累積脚曾,AOF會(huì)逐漸增長(zhǎng)东且,可能會(huì)占用巨大的空間。為此斟珊,Redis實(shí)現(xiàn)了AOF重寫(xiě)機(jī)制苇倡。AOF重寫(xiě)的出發(fā)點(diǎn)在于富纸,一段時(shí)間內(nèi)多某個(gè)key進(jìn)行若干次寫(xiě)操作,都會(huì)被記錄到AOF文件中旨椒,而當(dāng)前的數(shù)據(jù)僅包含一種狀態(tài)晓褪,那么可以將這段時(shí)間內(nèi)的寫(xiě)入操作進(jìn)行合并,這樣可以降低AOF文件的空間占用综慎。AOF重寫(xiě)相關(guān)的配置如下:
# 在進(jìn)行AOF重寫(xiě)的時(shí)候是否進(jìn)行fsync調(diào)用涣仿,yes-不調(diào)用fsync,no-調(diào)用fsync
no-appendfsync-on-rewrite no
# AOF重寫(xiě)的觸發(fā)條件:百分比-當(dāng)相比上次重寫(xiě)達(dá)到100%數(shù)據(jù)增長(zhǎng)時(shí)示惊,大小的絕對(duì)值-當(dāng)AOF文件增加達(dá)到64MB時(shí)
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
與RDB類(lèi)似好港,AOF重寫(xiě)也采用fork子進(jìn)程的方式,其步驟較RDB稍微復(fù)雜一些:
fork 出子進(jìn)程
子進(jìn)程在臨時(shí)文件中進(jìn)行AOF重寫(xiě)
父進(jìn)程同時(shí)保持兩個(gè)動(dòng)作:
繼續(xù)對(duì)所有的寫(xiě)請(qǐng)求米罚,向原AOF文件寫(xiě)入钧汹,保證數(shù)據(jù)的安全性
將所有新來(lái)的寫(xiě)請(qǐng)求記錄到一塊單獨(dú)的內(nèi)存緩沖區(qū)中。
當(dāng)子進(jìn)程完成重寫(xiě)后录择,父進(jìn)程收到通知拔莱,將內(nèi)存緩沖區(qū)中的新的變化追加到臨時(shí)文件中
將兩個(gè)文件進(jìn)行交換重命名,并向啟用新的AOF文件隘竭。
4.3 從Durability和可用性角度討論
4.3.1 RDB
持久性方面:用戶(hù)可以定義當(dāng)滿(mǎn)足某種條件時(shí)即進(jìn)行快照持久化塘秦,通常,考慮到性能問(wèn)題动看,這個(gè)時(shí)間間隔會(huì)設(shè)置為數(shù)十秒甚至分鐘級(jí)尊剔。因此RDB方式并不能提供很好的持久性,若在兩次持久化間發(fā)生故障菱皆,可能造成較大規(guī)模數(shù)據(jù)的丟失须误。
可用性方面:在不考慮首次RDB生成的情況下,首次以后的RDB文件的生成Redis的實(shí)現(xiàn)采用了雙文件的機(jī)制仇轻,即在進(jìn)行RDB時(shí)霹期,先生成新的臨時(shí)文件,當(dāng)新臨時(shí)文件生成完成時(shí)拯田,通過(guò)原子性的系統(tǒng)函數(shù)rename進(jìn)行重命名。因此甩十,在大部分的情況下(除了首次RDB)船庇,Redis都保證RDB文件是可用的。
4.3.2 AOF
AOF方式以追加的方式進(jìn)行持久化侣监,因此提供了較好的持久化數(shù)據(jù)可用性鸭轮。需要注意的是,采用AOF文件進(jìn)行數(shù)據(jù)恢復(fù)時(shí)橄霉,效率不如RDB窃爷。
持久性方面,與fsync觸發(fā)頻率配置相關(guān)。對(duì)于常用的everysec選項(xiàng)按厘,Redis至少保證2s的數(shù)據(jù)持久時(shí)間間隔医吊,因此,最差情況下逮京,最多有2s的數(shù)據(jù)是不可用的卿堂。對(duì)于always,則是命令級(jí)別懒棉;而對(duì)于no草描,依賴(lài)于操作系統(tǒng)的不同實(shí)現(xiàn),Linux的默認(rèn)實(shí)現(xiàn)中磁盤(pán)數(shù)據(jù)持久化的時(shí)間間隔為30s策严。
綜上穗慕,RDB在持久性方面不夠但持久化數(shù)據(jù)的可用性較好。AOF在持久性和可用性方面均表現(xiàn)良好妻导。
4.3 那么我該怎么選
魯迅曾經(jīng)說(shuō)過(guò)逛绵,離開(kāi)具體應(yīng)用場(chǎng)景談一項(xiàng)實(shí)現(xiàn)的優(yōu)劣都是耍流氓,持久化機(jī)制的選擇也是一樣栗竖∈畲啵總的來(lái)說(shuō),有四種選項(xiàng)供我們選擇:都不選(裸奔狐肢,飛一般的感覺(jué))添吗,選擇RDB,選擇AOF份名,我全都要(鰲拜臉)碟联。針對(duì)這四種進(jìn)行簡(jiǎn)要討論:
都不選。遇到的這種情況的應(yīng)用場(chǎng)景通常都是:我就緩存?zhèn)€數(shù)據(jù)僵腺,丟了就丟了鲤孵,數(shù)據(jù)庫(kù)里面反正還有,我要的是性能你明白我意思吧辰如。不可否認(rèn)普监,這是一種非常有效的辦法,但是琉兜,這里存在一個(gè)問(wèn)題:假如某天應(yīng)用發(fā)生了異常凯正,開(kāi)發(fā)同學(xué)懷疑是Redis數(shù)據(jù)有問(wèn)題導(dǎo)致的,但禍不單行(禍往往真的就不單行)豌蟋,還沒(méi)查到原因呢廊散,Redis重啟了,這種情況下可能就會(huì)變成無(wú)頭案梧疲。這個(gè)時(shí)候如果我們打開(kāi)了RDB允睹,并且通過(guò)定時(shí)任務(wù)或者其他手段對(duì)RDB文件進(jìn)行了備份运准,持久化就會(huì)變得非常香了。
這種情況可能很多同學(xué)還是會(huì)說(shuō)缭受,丟了就丟了胁澳,出現(xiàn)這種情況我認(rèn)了,一開(kāi)RDB贯涎,fork拖慢我的響應(yīng)听哭,得不償失啊。針對(duì)這種想法塘雳,首先陆盘,墨菲定律在這里舉起了他的小手。其次败明,從前面的討論可以得知隘马,fork影響Redis響應(yīng)的情況主要是對(duì)于單個(gè)實(shí)例存儲(chǔ)的數(shù)據(jù)量過(guò)大的情況。這里我們推薦可以通過(guò)采用更多的小內(nèi)存占用的機(jī)器構(gòu)成多實(shí)例的集群妻顶,而不是較少的大內(nèi)存機(jī)器構(gòu)成集群酸员。因?yàn)椋?. Redis是單線(xiàn)程處理命令的,理論上多臺(tái)機(jī)器能多用個(gè)核(畢竟缸多馬力大)讳嘱;2. 一臺(tái)低配的X86他也不貴啊幔嗦。
選擇RDB或者選擇AOF。二者擇其一沥潭,反而比較簡(jiǎn)單邀泉。從前面的討論我們知道,這兩者者的取舍钝鸽,無(wú)非是性能和持久性之間做權(quán)衡(沒(méi)辦法汇恤,只有付出才能得到),只要我們應(yīng)用的場(chǎng)景能夠容忍相應(yīng)時(shí)間的數(shù)據(jù)丟失拔恰,選擇對(duì)應(yīng)的持久化機(jī)制即可因谎。
我全都要。實(shí)際當(dāng)中颜懊,我們當(dāng)然希望構(gòu)建一個(gè)足夠健壯的系統(tǒng)财岔,我們可以綜合利用RDB適合用來(lái)備份以及恢復(fù)效率高和AOF提供的更加好的持久性保證。但是河爹,我們必須關(guān)注兩種同時(shí)存在的情況下是否會(huì)帶來(lái)額外的不利因素使鹅。從前面的討論我們知道,不論是RDB還是AOF昌抠,都是將數(shù)據(jù)持久化到硬盤(pán)上,但是硬盤(pán)的寫(xiě)入能力是有限的鲁僚,在兩種持久化方式都打開(kāi)的情況下炊苫,尤其是假如RDB和AOF重寫(xiě)同時(shí)發(fā)生裁厅,這個(gè)時(shí)候可能更容易造成達(dá)到硬盤(pán)的寫(xiě)入能力瓶頸的情況,如果無(wú)法在短時(shí)間內(nèi)完成文件寫(xiě)入侨艾,那么后續(xù)的fsync和write都可能阻塞执虹。這顯然是我們不愿意看到的(生產(chǎn)環(huán)境中,我就遇到過(guò)系統(tǒng)同時(shí)打開(kāi)兩種持久化的情況下唠梨,由于達(dá)到硬盤(pán)寫(xiě)入瓶頸而導(dǎo)致的阻塞)袋励。當(dāng)然,這并不是說(shuō)我們不建議同時(shí)打開(kāi)兩種持久化当叭,假設(shè)我們擁有較高性能的硬盤(pán)茬故,同時(shí)Redis面臨的寫(xiě)入壓力并不是很高,完全可以采用兩種持久化結(jié)合的方式來(lái)獲得一個(gè)更加穩(wěn)健的系統(tǒng)蚁鳖。
5. 總結(jié)
以上磺芭,我們對(duì)Redis的持久化進(jìn)行了討論。主要涉及以下幾個(gè)方面的內(nèi)容:
首先醉箕,為了更好的理解持久化钾腺,介紹了用戶(hù)空間、內(nèi)核空間以及內(nèi)核緩沖區(qū)幾個(gè)Linux內(nèi)存劃分涉及的基礎(chǔ)概念讥裤,以及Linux在進(jìn)程fork過(guò)程中涉及的寫(xiě)時(shí)復(fù)制技術(shù)放棒。這兩點(diǎn)內(nèi)容主要涉及Redis在持久化過(guò)程中數(shù)據(jù)的轉(zhuǎn)移過(guò)程,以及RDB文件寫(xiě)入以及AOF重寫(xiě)的過(guò)程己英。
其次间螟,我們分步驟討論了持久化過(guò)程中數(shù)據(jù)在機(jī)器上的流轉(zhuǎn)過(guò)程,主要是從用戶(hù)態(tài)內(nèi)存空間轉(zhuǎn)移到內(nèi)核緩沖區(qū)再轉(zhuǎn)移到最終的持久化設(shè)備剧辐。在轉(zhuǎn)移的過(guò)程中涉及到應(yīng)用程序觸發(fā)不同的系統(tǒng)調(diào)用寒亥。
再次,我們討論了持久化的目的以及如何衡量持久化能力荧关,可以從Durability以及可用性?xún)蓚€(gè)方面進(jìn)行溉奕。
最后,我們對(duì)Redis兩種持久化模式參數(shù)配置忍啤、機(jī)制進(jìn)行了對(duì)比介紹加勤,然后采用Durability和可用性進(jìn)行了對(duì)比分析,最終討論了如何結(jié)合實(shí)際應(yīng)用場(chǎng)景選擇持久化機(jī)制同波。在持久化機(jī)制的實(shí)際選擇時(shí)鳄梅,需要結(jié)合應(yīng)用場(chǎng)景進(jìn)行具體分析,從性能要求未檩、持久化能力要求以及系統(tǒng)健壯性幾個(gè)方面綜合考慮戴尸,做出適合實(shí)際應(yīng)用場(chǎng)景的選擇。
本文大量參考了Redis作者對(duì)Redis持久化的討論冤狡,參考文檔如下:
【REF】http://oldblog.antirez.com/post/redis-persistence-demystified.html