redis持久化一般支持兩種方式,快照持久化(rdb)和日志持久化(aof)
rdb持久化
1. rdb的配置選項
save 900 1 900秒內(nèi),有一條寫入,則產(chǎn)生快照
save 300 300秒內(nèi),有1000次寫入,則產(chǎn)生快照
save 60 10000 如果60秒內(nèi)有10000次寫入,則產(chǎn)生快照
這三個選項如果都屏蔽,則rdb禁用
stop-writes-on-bgsave-error yes 后臺備份進(jìn)程出錯時,主進(jìn)程停不停止寫入
rdbcompression yes 導(dǎo)出的rdb文件是否壓縮
rdbchecksum yes 導(dǎo)入rdb恢復(fù)數(shù)據(jù)時,要不要檢測rdb的完整性
dbfilename dump.rdb 導(dǎo)出來的rdb文件名
dir ./ rdb的放置路徑
2. 快照生成原理
Redis借助了fork命令的copy on write機(jī)制。在生成快照時,將當(dāng)前進(jìn)程fork出一個子進(jìn)程,然后在子進(jìn)程中循環(huán)所有的數(shù)據(jù),將數(shù)據(jù)寫成為RDB文件。
3. Client 也可以使用save或者bgsave命令通知redis做一次快照持久化。
save操作是在主線程中保存快照的,由于redis是用一個主線程來處理所有 client的請求,這種方式會阻塞所有client請求。所以不推薦使用。另一點需要注意的是,每次快照持久化都是將內(nèi)存數(shù)據(jù)完整寫入到磁盤一次,并不 是增量的只同步臟數(shù)據(jù)。如果數(shù)據(jù)量大的話,而且寫操作比較多,必然會引起大量的磁盤io操作,可能會嚴(yán)重影響性能。
Redis的RDB文件不會壞掉,因為其寫操作是在一個新進(jìn)程中進(jìn)行的。當(dāng)生成一個新的RDB文件時,Redis生成的子進(jìn)程會先將數(shù)據(jù)寫到一個臨時文件中,然后通過原子性rename系統(tǒng)調(diào)用將臨時文件重命名為RDB文件。這樣在任何時候出現(xiàn)故障,Redis的RDB文件都總是可用的。并且Redis的RDB文件也是Redis主從同步內(nèi)部實現(xiàn)中的一環(huán):
4. 快照持久化過程
1).redis調(diào)用fork,現(xiàn)在有了子進(jìn)程和父進(jìn)程。
2).父進(jìn)程繼續(xù)處理client請求,子進(jìn)程負(fù)責(zé)將內(nèi)存內(nèi)容寫入到臨時文件。由于os的寫時復(fù)制機(jī)制(copy on write)父子進(jìn)程會共享相同的物理頁面,當(dāng)父進(jìn)程處理寫請求時os會為父
進(jìn)程要修改的頁面創(chuàng)建副本,而不是寫共享的頁面。所以子進(jìn)程的地址空間內(nèi)的數(shù) 據(jù)是fork時刻整個數(shù)據(jù)庫的一個快照。
3).當(dāng)子進(jìn)程將快照寫入臨時文件完畢后,用臨時文件替換原來的快照文件,然后子進(jìn)程退出。
5. rdb的不足
就是一旦數(shù)據(jù)庫出現(xiàn)問題,那么我們的RDB文件中保存的數(shù)據(jù)并不是全新的,從上次RDB文件生成到Redis停機(jī)這段時間的數(shù)據(jù)全部丟掉了(因為刷寫機(jī)制還沒有發(fā)出)。RDB就是Snapshot快照存儲,是默認(rèn)的持久化方式。
aof持久化
1. aof持久化相關(guān)參數(shù)
appendonly no # 是否打開 aof日志功能
appendfsync always # 每1個命令,都立即同步到aof. 安全,速度慢
appendfsync everysec # 折衷方案,每秒寫1次
appendfsync no # 寫入工作交給操作系統(tǒng),由操作系統(tǒng)判斷緩沖區(qū)大小,統(tǒng)一寫入到aof. 同步頻率低,速度快,
no-appendfsync-on-rewrite yes: # 正在導(dǎo)出rdb快照的過程中,要不要停止同步aof
auto-aof-rewrite-percentage 100 #aof文件大小比起上次重寫時的大小,增長率100%時,重寫
auto-aof-rewrite-min-size 64mb #aof文件,至少超過64M時,重寫
2. AOF(Append Only File)<二進(jìn)制文件>比RDB方式有更好的持久化性。
由于在使用AOF持久化方式時,Redis會將每一個收到的寫命令都通過Write函數(shù)追加到文件最后,類似于MySQL的binlog。當(dāng)Redis重啟是會通過重新執(zhí)行文件中保存的寫命令來在內(nèi)存中重建整個數(shù)據(jù)庫的內(nèi)容。
AOF的完全持久化方式同時也帶來了另一個問題,持久化文件會變得越來越大。(比如我們調(diào)用INCR test命令100次,文件中就必須保存全部的100條命令,但其實99條都是多余的。因為要恢復(fù)數(shù)據(jù)庫的狀態(tài)其實文件中保存一條SET test 100就夠了)。為了合并重寫AOF的持久化文件,Redis提供了bgrewriteaof命令。收到此命令后Redis將使用與快照類似的方式將內(nèi)存中的數(shù)據(jù)以命令的方式保存到臨時文件中,最后替換原來的文件,以此來實現(xiàn)控制AOF文件的合并重寫。由于是模擬快照的過程,因此在重寫AOF文件時并沒有讀取舊的AOF文件,而是將整個內(nèi)存中的數(shù)據(jù)庫內(nèi)容用命令的方式重寫了一個新的AOF文件。
3. AOF持久化過程:
1). redis調(diào)用fork ,現(xiàn)在有父子兩個進(jìn)程
2). 子進(jìn)程根據(jù)內(nèi)存中的數(shù)據(jù)庫快照,往臨時文件中寫入重建數(shù)據(jù)庫狀態(tài)的命令
3).父進(jìn)程繼續(xù)處理client請求,除了把寫命令寫入到原來的aof文件中。同時把收到的寫命令緩存起來。這樣就能保證如果子進(jìn)程重寫失敗的話并不會出問題。
4).當(dāng)子進(jìn)程把快照內(nèi)容寫入已命令方式寫到臨時文件中后,子進(jìn)程發(fā)信號通知父進(jìn)程。然后父進(jìn)程把緩存的寫命令也寫入到臨時文件。
5).現(xiàn)在父進(jìn)程可以使用臨時文件替換老的aof文件,并重命名,后面收到的寫命令也開始往新的aof文件中追加。
兩者的區(qū)別
從上面看出,RDB和AOF操作都是順序IO操作,性能都很高。而同時在通過RDB文件或者AOF日志進(jìn)行數(shù)據(jù)庫恢復(fù)的時候,也是順序的讀取數(shù)據(jù)加載到內(nèi)存中。所以也不會造成磁盤的隨機(jī)讀。
到底選擇什么呢?下面是來自官方的建議:
通常,如果你要想提供很高的數(shù)據(jù)保障性,那么建議你同時使用兩種持久化方式。如果你可以接受災(zāi)難帶來的幾分鐘的數(shù)據(jù)丟失,那么你可以僅使用RDB。
很多用戶僅使用了AOF,但是我們建議,既然RDB可以時不時的給數(shù)據(jù)做個完整的快照,并且提供更快的重啟,所以最好還是也使用RDB。
在數(shù)據(jù)恢復(fù)方面:
RDB的啟動時間會更短,原因有兩個:
一是RDB文件中每一條數(shù)據(jù)只有一條記錄,不會像AOF日志那樣可能有一條數(shù)據(jù)的多次操作記錄。所以每條數(shù)據(jù)只需要寫一次就行了。
另一個原因是RDB文件的存儲格式和Redis數(shù)據(jù)在內(nèi)存中的編碼格式是一致的,不需要再進(jìn)行數(shù)據(jù)編碼工作,所以在CPU消耗上要遠(yuǎn)小于AOF日志的加載。
注意:
上面說了RDB快照的持久化,需要注意:在進(jìn)行快照的時候(save),fork出來進(jìn)行dump操作的子進(jìn)程會占用與父進(jìn)程一樣的內(nèi)存,真正的copy-on-write,對性能的影響和內(nèi)存的耗用都是比較大的。比如機(jī)器8G內(nèi)存,Redis已經(jīng)使用了6G內(nèi)存,這時save的話會再生成6G,變成12G,大于系統(tǒng)的8G。這時候會發(fā)生交換;要是虛擬內(nèi)存不夠則會崩潰,導(dǎo)致數(shù)據(jù)丟失。所以在用redis的時候一定對系統(tǒng)內(nèi)存做好容量規(guī)劃。
目前,通常的設(shè)計思路是利用Replication機(jī)制來彌補(bǔ)aof、snapshot性能上的不足,達(dá)到了數(shù)據(jù)可持久化。即Master上Snapshot和AOF都不做,來保證Master的讀寫性能,而Slave上則同時開啟Snapshot和AOF來進(jìn)行持久化,保證數(shù)據(jù)的安全性。