您好,登錄后才能下訂單哦!
如何進(jìn)行Redis持久化RDB和AOF的分析,相信很多沒有經(jīng)驗的人對此束手無策,為此本文總結(jié)了問題出現(xiàn)的原因和解決方法,通過這篇文章希望你能解決這個問題。
Redis持久化方案
Redis是內(nèi)存數(shù)據(jù)庫,數(shù)據(jù)都是存儲在內(nèi)存中,為了避免進(jìn)程退出導(dǎo)致數(shù)據(jù)的永久丟失,需要定期將Redis中的數(shù)據(jù)以某種形式(數(shù)據(jù)或命令)從內(nèi)存保存到硬盤。當(dāng)下次Redis重啟時,利用持久化文件實現(xiàn)數(shù)據(jù)恢復(fù)。除此之外,為了進(jìn)行災(zāi)難備份,可以將持久化文件拷貝到一個遠(yuǎn)程位置。
Redis提供了多種不同級別的持久化方式:一種是RDB,另一種是AOF。
RDB持久化可以在指定的時間間隔內(nèi)生成數(shù)據(jù)集的時間點快照(point-in-time snapshot),將數(shù)據(jù)庫的快照(snapshot)以二進(jìn)制的方式保存到磁盤中。
AOF持久化記錄服務(wù)器執(zhí)行的所有更改操作命令,AOF文件中的命令全部以Redis協(xié)議的格式來保存,新命令會被追加到文件的末尾。Redis還可以在后臺對AOF文件進(jìn)行重寫(rewrite),使得AOF文件的體積不會超出保存數(shù)據(jù)集狀態(tài)所需的實際大小。
Redis可以同時使用AOF持久化和RDB持久化。在這種情況下,當(dāng)Redis重啟時,它會優(yōu)先使用AOF文件來還原數(shù)據(jù)集,因為AOF文件保存的數(shù)據(jù)集通常比RDB文件所保存的數(shù)據(jù)集更完整。你甚至可以關(guān)閉持久化功能,讓數(shù)據(jù)只在服務(wù)器運行時存在。
了解RDB持久化和AOF持久化之間的異同是非常重要的,以下幾個小節(jié)將詳細(xì)地介紹這這兩種持久化功能,并對它們的相同和不同之處進(jìn)行說明。
RDB快照
下面我們說一下Redis的第一個持久化策略,RDB快照。Redis支持將當(dāng)前內(nèi)存數(shù)據(jù)的快照存成一個數(shù)據(jù)文件的持久化機(jī)制,而一個持續(xù)寫入的數(shù)據(jù)庫如何生成快照呢?Redis巧妙地借助了fork命令的寫時復(fù)制(copy on write)機(jī)制,將當(dāng)前進(jìn)程fork出一個子進(jìn)程,子進(jìn)程根據(jù)內(nèi)存快照,循環(huán)將數(shù)據(jù)持久化為RDB文件。
在默認(rèn)情況下, Redis將數(shù)據(jù)庫快照保存在根目錄下,名字為dump.rdb的二進(jìn)制文件中??赏ㄟ^參數(shù)dir配置指定保存目錄,dbfilename指定文件名。你可以對Redis進(jìn)行設(shè)置,如“save N M”,表示N秒內(nèi)數(shù)據(jù)項中有M個改動時這一條件被滿足時,自動保存一次數(shù)據(jù)集。比如你可以配置當(dāng)10分鐘以內(nèi)有100次寫入就生成快照,也可以配置當(dāng)1分鐘內(nèi)有1000次寫入就生成快照,支持可以多個規(guī)則一起生效,當(dāng)匹配到哪個就哪個規(guī)則生效。這些規(guī)則的定義就在Redis的配置文件中,你也可以通過Redis的CONFIG SET命令在Redis運行時設(shè)置規(guī)則,不需要重啟Redis。
比如說,以下設(shè)置會讓Redis在滿足“60秒內(nèi)有至少有1000個鍵被改動”這一條件時,自動保存一次數(shù)據(jù)集:
save 60 1000
你也可以通過調(diào)用SAVE或者BGSAVE,手動讓Redis進(jìn)行數(shù)據(jù)集保存操作。SAVE命令執(zhí)行一個同步操作,以RDB文件的方式保存所有數(shù)據(jù)的快照,很少在生產(chǎn)環(huán)境直接使用SAVE命令,因為它會阻塞所有的客戶端的請求,不要在生產(chǎn)環(huán)境使用,可以使用BGSAVE命令代替。BGSAVE命令執(zhí)行過程中是通過fork一個子進(jìn)程來完成的,所以不會阻塞客戶端請求,只有fork子進(jìn)程時會阻塞服務(wù)器。另外,在自動觸發(fā)RDB持久化時,Redis也會選擇BGSAVE而不是SAVE來進(jìn)行持久化。
Redis自動RDB持久化在其內(nèi)部是通過serverCron周期性操作函數(shù)、dirty計數(shù)器、和lastsave時間戳來實現(xiàn)的。其中serverCron每100ms執(zhí)行一次,檢查服務(wù)器狀態(tài),其中就包括檢查“save N M”是否滿足條件,如果滿足就執(zhí)行BGSAVE;當(dāng)然也包括AOF重寫檢查。dirty計數(shù)器是Redis服務(wù)器維持的一個狀態(tài),記錄了上一次執(zhí)行BGSAVE/SAVE命令后,服務(wù)器狀態(tài)進(jìn)行了多少次修改(包括增刪改),而當(dāng)BGSAVE/SAVE執(zhí)行完成后,會將dirty重新置為0。lastsave時間戳也是Redis服務(wù)器維持的一個狀態(tài),記錄的是上一次成功執(zhí)行BGSAVE/SAVE的時間,當(dāng)前時間減去lastsave需滿足M。
除了手動和自動以外,還有一些其他情況會觸發(fā)BGSAVE:
在主從復(fù)制場景下,如果從節(jié)點執(zhí)行全量復(fù)制操作,則主節(jié)點會執(zhí)行BGSAVE命令,并將rdb文件發(fā)送給從節(jié)點。
執(zhí)行shutdown命令時,自動執(zhí)行rdb持久化。
另外需要了解的,因為其寫操作是在一個新進(jìn)程中進(jìn)行的,當(dāng)生成一個新的RDB文件時,Redis生成的子進(jìn)程會先將數(shù)據(jù)寫到一個臨時文件中,然后通過原子性rename系統(tǒng)調(diào)用將臨時文件重命名為RDB文件,這樣在任何時候出現(xiàn)故障,Redis的RDB文件都總是可用的。
這種持久化方式被稱為快照(snapshot)。但是,我們可以很明顯的看到,RDB有他的不足,就是一旦數(shù)據(jù)庫出現(xiàn)問題,那么我們的RDB文件中保存的數(shù)據(jù)并不是全新的,從上次RDB文件生成到Redis停機(jī)這段時間的數(shù)據(jù)全部丟掉了。在某些業(yè)務(wù)下,如果可以忍受間隔內(nèi)數(shù)據(jù)丟失,我們也推薦這些業(yè)務(wù)使用RDB的方式進(jìn)行持久化,因為開啟RDB的代價并不高。但是對于另外一些對數(shù)據(jù)安全性要求極高的應(yīng)用,無法容忍數(shù)據(jù)丟失的應(yīng)用,RDB就無能為力了,所以Redis引入了另一個重要的持久化機(jī)制,AOF日志方式持久化。
為了盡可能使RDB文件體積減小,Redis默認(rèn)采用LZF算法對RDB文件進(jìn)行壓縮。雖然壓縮耗時,但是可以大大減小RDB文件的體積,因此壓縮默認(rèn)開啟,參數(shù)為rdbcompression。需要注意的是,RDB文件的壓縮并不是針對整個文件進(jìn)行的,而是對數(shù)據(jù)庫中的字符串進(jìn)行的,且只有在字符串達(dá)到一定長度(20字節(jié))時才會進(jìn)行。
除了壓縮,你也可以檢驗RDB文件,通過參數(shù)rdbchecksum設(shè)置,默認(rèn)為yes。在寫入文件和讀取文件時都起作用,關(guān)閉checksum在寫入文件和啟動文件時大約能帶來10%的性能提升,但是數(shù)據(jù)損壞時無法發(fā)現(xiàn)。
另外,當(dāng)bgsave出現(xiàn)錯誤時,Redis是否停止執(zhí)行寫命令。Redis提供了一個參數(shù)stop-writes-on-bgsave-error,設(shè)置為yes,則當(dāng)硬盤出現(xiàn)問題時,可以及時發(fā)現(xiàn),避免數(shù)據(jù)的大量丟失;設(shè)置為no,則Redis無視bgsave的錯誤繼續(xù)執(zhí)行寫命令,當(dāng)對Redis服務(wù)器的系統(tǒng)(尤其是硬盤)使用了監(jiān)控時,該選項考慮設(shè)置為no。
說說FORK的開銷?
父進(jìn)程通過fork操作可以創(chuàng)建子進(jìn)程,第一代Unix系統(tǒng)實現(xiàn)了一種傻瓜式的進(jìn)程創(chuàng)建:當(dāng)執(zhí)行fork系統(tǒng)調(diào)用時,內(nèi)核復(fù)制父進(jìn)程的整個用戶空間并把復(fù)制得到的那一份分配給子進(jìn)程。這種行為時非常耗時的,因為它需要完成以下幾項任務(wù):為子進(jìn)程的頁表分配頁面、為子進(jìn)程的頁分配頁面、初始化子進(jìn)程的頁表、把父進(jìn)程的頁復(fù)制到子進(jìn)程對應(yīng)的頁中。
現(xiàn)在 Linux 的fork()使用寫時拷貝(copy-on-write)頁實現(xiàn)。寫時拷貝是一種可以推遲甚至免除拷貝數(shù)據(jù)的技術(shù)。內(nèi)核此時并不復(fù)制整個進(jìn)程地址空間,而是讓父進(jìn)程和子進(jìn)程共享同一個拷貝。只有在需要寫入的時候,數(shù)據(jù)才會被復(fù)制,從而使各個進(jìn)程擁有各自的拷貝。也就是說,資源的復(fù)制只有在需要寫入的時候才進(jìn)行,在此之前,只是以只讀方式共享。這種技術(shù)使地址空間上的頁的拷貝被推遲到實際發(fā)生寫入的時候。所以就算fork很大內(nèi)存的進(jìn)程,對內(nèi)存的消耗和耗時都很小。
現(xiàn)在雖然fork時,子進(jìn)程不會復(fù)制父進(jìn)程的數(shù)據(jù)空間,但是會復(fù)制內(nèi)存頁表(頁表相當(dāng)于內(nèi)存的索引、目錄);父進(jìn)程的數(shù)據(jù)空間越大,內(nèi)存頁表越大,fork時復(fù)制耗時也會越多。這個問題也是導(dǎo)致Redis內(nèi)存不宜過大的原因之一,當(dāng)然還有導(dǎo)致故障恢復(fù)時間延長也是Redis內(nèi)存不宜過大的原因。
AOF日志
通過上面的分析,我們知道RDB快照有大概率丟失最近寫入、且仍未保存到快照中的那些數(shù)據(jù)。盡管對于某些程序來說,數(shù)據(jù)安全并不是最重要的考慮因素,但是對于那些追求數(shù)據(jù)安全的程序來說,快照功能就不太適用了。從1.1版本開始,Redis增加了一種實時性更好的持久化方式,即AOF持久化。AOF日志的全稱是append only file,從名字上我們就能看出來,它是一個追加寫入的日志文件。與RDB相比,AOF的實時性更好,因此已成為主流的持久化方案。
AOF文件與MySQL數(shù)據(jù)庫的binlog不同的是,AOF是一種純文本格式,具有兼容性好、可讀性強(qiáng)、容易處理、操作簡單避免二次開銷等優(yōu)點,它記錄的內(nèi)容就是一個個的Redis標(biāo)準(zhǔn)命令。開啟AOF持久化命令如下:
appendonly yes appendfilename "appendonly.aof"
從現(xiàn)在開始,每當(dāng)Redis執(zhí)行一個改變數(shù)據(jù)集的命令時(比如SET),這個命令就會被追加到AOF文件的末尾。這樣的話,當(dāng)Redis重新啟時,程序就可以通過重新執(zhí)行AOF文件中的命令來達(dá)到重建數(shù)據(jù)集的目的。
由于需要記錄Redis的每條寫命令,因此AOF不需要觸發(fā),下面介紹AOF的執(zhí)行流程:
命令追加(append)
Redis先將寫命令追加到緩沖區(qū),而不是直接寫入文件,主要是為了避免每次有寫命令都直接寫入硬盤,導(dǎo)致硬盤IO成為Redis負(fù)載的瓶頸。
文件寫入(write)和文件同步(sync)
Redis提供了多種AOF緩存區(qū)的同步文件策略,策略涉及到操作系統(tǒng)的write函數(shù)和fsync函數(shù),說明如下:
為了提高文件寫入效率,在現(xiàn)代操作系統(tǒng)中,當(dāng)用戶調(diào)用write函數(shù)將數(shù)據(jù)寫入文件時,操作系統(tǒng)通常會將數(shù)據(jù)暫存到一個內(nèi)存緩沖區(qū)里,當(dāng)緩沖區(qū)被填滿或超過了指定時限后,才真正將緩沖區(qū)的數(shù)據(jù)寫入到硬盤里。這樣的操作雖然提高了效率,但也帶來了安全問題:如果計算機(jī)停機(jī),內(nèi)存緩沖區(qū)中的數(shù)據(jù)會丟失;因此系統(tǒng)同時提供了fsync、fdatasync等同步函數(shù),可以強(qiáng)制操作系統(tǒng)立刻將緩沖區(qū)中的數(shù)據(jù)寫入到硬盤里,從而確保數(shù)據(jù)的安全性。
AOF緩存區(qū)的同步文件策略由參數(shù)appendfsync控制,各個值的含義如下:
always:命令寫入aof_buf后立即調(diào)用系統(tǒng)fsync操作同步到AOF文件,fsync完成后線程返回。這種情況下,每次有寫命令都要同步到AOF文件,硬盤IO成為性能瓶頸,Redis只能支持大約幾百TPS寫入,嚴(yán)重降低了Redis的性能;即便是使用固態(tài)硬盤(SSD),每秒大約也只能處理幾萬個命令,而且會大大降低SSD的壽命。
no:命令寫入aof_buf后調(diào)用系統(tǒng)write操作,不對AOF文件做fsync同步;同步由操作系統(tǒng)負(fù)責(zé),通常同步周期為30秒。這種情況下,文件同步的時間不可控,且緩沖區(qū)中堆積的數(shù)據(jù)會很多,數(shù)據(jù)安全性無法保證。
everysec:命令寫入aof_buf后調(diào)用系統(tǒng)write操作,write完成后線程返回;fsync同步文件操作由專門的線程每秒調(diào)用一次。everysec是前述兩種策略的折中,是性能和數(shù)據(jù)安全性的平衡,因此是Redis的默認(rèn)配置,也是我們推薦的配置。
文件重寫(rewrite)
因為AOF的運作方式是不斷地將命令追加到文件的末尾,所以隨著寫入命令的不斷增加,AOF文件的體積也會變得越來越大。舉個例子,如果你對一個計數(shù)器調(diào)用了100次INCR,那么僅僅是為了保存這個計數(shù)器的當(dāng)前值,AOF文件就需要使用100條記錄(entry)。然而在實際上,只使用一條SET命令已經(jīng)足以保存計數(shù)器的當(dāng)前值了,其余99條記錄實際上都是多余的。另外還有一些過期的數(shù)據(jù),無效的數(shù)據(jù)也都是可以去除。
過大的AOF文件不僅會影響服務(wù)器的正常運行,也會導(dǎo)致數(shù)據(jù)恢復(fù)需要的時間過長。為了處理這種情況,Redis支持一種有趣的特性,可以在不打斷服務(wù)客戶端的情況下,對AOF文件進(jìn)行重建(rebuild)。執(zhí)行BGREWRITEAOF命令, Redis將生成一個新的AOF文件, 這個文件包含重建當(dāng)前數(shù)據(jù)集所需的最少命令。
AOF REWRITE(重寫)生成過程和RDB快照類似,都巧妙地利用了寫時復(fù)制機(jī)制。同樣也是fork一個子進(jìn)程(此時主線程是阻塞的),子進(jìn)程根據(jù)內(nèi)存快照,按照命令合并規(guī)則寫入到新的AOF文件。當(dāng)主進(jìn)程fork完子線程后繼續(xù)接受請求,所有寫命令依然寫入AOF緩沖區(qū)(aof_buf),并根據(jù)appendfsync策略同步到硬盤,保證原有AOF機(jī)制的正確。但由于fork操作使用寫時復(fù)制技術(shù),子進(jìn)程只能共享fork操作時的內(nèi)存數(shù)據(jù)。由于父進(jìn)程依然在響應(yīng)命令,因此Redis使用AOF重寫緩沖區(qū)(aof_rewrite_buf)保存這部分新日志,防止新AOF文件生成期間丟失這部分?jǐn)?shù)據(jù)。也就是說,bgrewriteaof執(zhí)行期間,Redis的寫命令同時追加到aof_buf和aof_rewirte_buf兩個緩沖區(qū)。
當(dāng)子進(jìn)程寫完新的AOF文件后,向父進(jìn)程發(fā)信號,父進(jìn)程更新統(tǒng)計信息,具體可以通過info persistence查看。然后父進(jìn)程把AOF重寫緩沖區(qū)的數(shù)據(jù)寫入到新的AOF文件,這樣就保證了新AOF文件所保存的數(shù)據(jù)庫狀態(tài)和服務(wù)器當(dāng)前狀態(tài)一致。然后調(diào)用原子性的rename命令用新的AOF文件取代老的AOF文件,完成AOF重寫。
這里需要注意,因為由主進(jìn)程把a(bǔ)of_rewrite_buf緩存追加到新日志文件。主進(jìn)程追加日志時,不會處理其他請求,如果aof_rewrite_buf特別大,例如幾百M,也可能造成Redis幾秒甚至幾十秒不響應(yīng)。
從上面的流程我們能夠看到,RDB和AOF操作都是順序IO操作,性能都很高。而在通過RDB文件或者AOF日志進(jìn)行數(shù)據(jù)庫恢復(fù)的時候,也是順序的讀取數(shù)據(jù)加載到內(nèi)存中。所以也不會造成磁盤的隨機(jī)讀。
文件重寫的觸發(fā),分為手動觸發(fā)和自動觸發(fā):
手動觸發(fā):直接調(diào)用bgrewriteaof命令,該命令的執(zhí)行與bgsave有些類似:都是fork子進(jìn)程進(jìn)行具體的工作,且都只有在fork時阻塞。
自動觸發(fā):根據(jù)auto-aof-rewrite-min-size和auto-aof-rewrite-percentage參數(shù),以及aof_current_size和aof_base_size狀態(tài)確定觸發(fā)時機(jī)。
auto-aof-rewrite-min-size表示執(zhí)行AOF重寫時,文件的最小體積,默認(rèn)值為64MB。
auto-aof-rewrite-percentage表示執(zhí)行AOF重寫時,當(dāng)前AOF大?。碼of_current_size)和上一次重寫時AOF大?。╝of_base_size)的比值,即增長比例達(dá)到設(shè)定值。
只有當(dāng)auto-aof-rewrite-min-size和auto-aof-rewrite-percentage兩個參數(shù)同時滿足時,才會自動觸發(fā)AOF重寫,即bgrewriteaof操作。
其中,參數(shù)可以通過config get命令查看:
127.0.0.1:6391> CONFIG GET auto-aof-rewrite-min-size 1) "auto-aof-rewrite-min-size" 2) "64000000" 127.0.0.1:6391> CONFIG GET auto-aof-rewrite-percentage 1) "auto-aof-rewrite-percentage" 2) "100"
狀態(tài)可以通過info persistence查看:
127.0.0.1:6379> info persistence # Persistence aof_enabled:1 aof_rewrite_in_progress:0 aof_rewrite_scheduled:0 aof_last_rewrite_time_sec:0 aof_current_rewrite_time_sec:-1 aof_last_bgrewrite_status:ok aof_last_write_status:ok aof_current_size:40876638 aof_base_size:2217565 aof_pending_rewrite:0 aof_buffer_length:0 aof_rewrite_buffer_length:0 aof_pending_bio_fsync:0 aof_delayed_fsync:0
另外在aof rewrite過程中,是否采取增量”文件同步”策略,由參數(shù)aof-rewrite-incremental-fsync控制,默認(rèn)為”yes”,而且必須為yes。rewrite過程中,每32M數(shù)據(jù)進(jìn)行一次文件同步,這樣可以減少”aof大文件”寫入對磁盤的操作次數(shù)。
bgrewriteaof機(jī)制,在一個子進(jìn)程中進(jìn)行aof的重寫,從而不阻塞主進(jìn)程對其余命令的處理,同時解決了aof文件過大問題?,F(xiàn)在問題出現(xiàn)了,同時在執(zhí)行bgrewriteaof操作和主進(jìn)程寫aof文件的操作,兩者都會操作磁盤,而bgrewriteaof往往會涉及大量磁盤操作,這樣就會造成主進(jìn)程在寫aof文件的時候出現(xiàn)阻塞的情形,現(xiàn)在no-appendfsync-on-rewrite參數(shù)出場了。
如果該參數(shù)設(shè)置為no,是最安全的方式,不會丟失數(shù)據(jù),但是要忍受阻塞的問題。如果設(shè)置為yes呢?這就相當(dāng)于將appendfsync設(shè)置為no,這說明并沒有執(zhí)行磁盤操作,只是寫入了緩沖區(qū),因此這樣并不會造成阻塞(因為沒有競爭磁盤),但是如果這個時候Redis掛掉,就會丟失數(shù)據(jù)。丟失多少數(shù)據(jù)呢?在Linux的操作系統(tǒng)的默認(rèn)設(shè)置下,最多會丟失30s的數(shù)據(jù)。因此,如果應(yīng)用系統(tǒng)無法忍受延遲,而可以容忍少量的數(shù)據(jù)丟失,則設(shè)置為yes。如果應(yīng)用系統(tǒng)無法忍受數(shù)據(jù)丟失,則設(shè)置為no。
AOF刷新策略?
前面提到過,在AOF中,如果AOF緩沖區(qū)的文件同步策略為everysec,則:在主線程中,命令寫入aof_buf后調(diào)用系統(tǒng)write操作,write完成后主線程返回;fsync同步文件操作由專門的文件同步線程每秒調(diào)用一次。這種做法的問題在于,如果硬盤負(fù)載過高,那么fsync操作可能會超過1s;如果Redis主線程持續(xù)高速向aof_buf寫入命令,硬盤的負(fù)載可能會越來越大,IO資源消耗更快;如果此時Redis進(jìn)程異常退出,丟失的數(shù)據(jù)也會越來越多,可能遠(yuǎn)超過1s。
為此,Redis的處理策略是這樣的:主線程每次進(jìn)行AOF會對比上次fsync成功的時間;如果距上次不到2s(也就是延遲了1s),主線程直接返回;如果超過2s,則主線程阻塞直到上一次fsync同步完成。因此,如果系統(tǒng)硬盤負(fù)載過大導(dǎo)致fsync速度太慢,會導(dǎo)致Redis主線程的阻塞;此外,使用everysec配置,AOF最多可能丟失2s的數(shù)據(jù),而不是1s。具體看Redis AOF刷新策略分析
AOF追加阻塞問題定位的方法,監(jiān)控info Persistence中的aof_delayed_fsync,當(dāng)AOF追加阻塞發(fā)生時(即主線程等待fsync而阻塞),該指標(biāo)累加。另外,AOF阻塞時的Redis日志: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.
如果AOF追加阻塞頻繁發(fā)生,說明系統(tǒng)的硬盤負(fù)載太大;可以考慮更換IO速度更快的硬盤,或者通過IO監(jiān)控分析工具對系統(tǒng)的IO負(fù)載進(jìn)行分析。
對于pipelining有什么不同?
對于pipelining的操作,其具體過程是客戶端一次性發(fā)送N個命令,然后等待這N個命令的返回結(jié)果被一起返回。通過采用pipilining就意味著放棄了對每一個命令的返回值確認(rèn)。由于在這種情況下,N個命令是在同一個執(zhí)行過程中執(zhí)行的。所以當(dāng)設(shè)置appendfsync為everysec時,可能會有一些偏差,因為這N個命令可能執(zhí)行時間超過1秒甚至2秒。但是可以保證的是,最長時間不會超過這N個命令的執(zhí)行時間和。
如果AOF文件出錯了,怎么辦?
服務(wù)器可能在程序正在對AOF文件進(jìn)行寫入時停機(jī),如果停機(jī)造成了 AOF 文件出錯(corrupt),那么Redis在重啟時會拒絕載入這個AOF文件, 從而確保數(shù)據(jù)的一致性不會被破壞。當(dāng)發(fā)生這種情況時,可以用以下方法來修復(fù)出錯的 AOF 文件:為現(xiàn)有的AOF文件創(chuàng)建一個備份。然后使用Redis附帶的redis-check-aof --fix程序?qū)υ瓉淼腁OF文件進(jìn)行修復(fù)。
然后可選使用 diff -u 對比修復(fù)后的 AOF 文件和原始 AOF 文件的備份,查看兩個文件之間的不同之處。再次重啟Redis服務(wù)器,等待服務(wù)器載入修復(fù)后的AOF文件,并進(jìn)行數(shù)據(jù)恢復(fù)。
但如果是AOF文件結(jié)尾不完整(機(jī)器突然宕機(jī)等容易導(dǎo)致文件尾部不完整),且aof-load-truncated參數(shù)開啟,則日志中會輸出警告,Redis忽略掉AOF文件的尾部,啟動成功。aof-load-truncated參數(shù)默認(rèn)是開啟的。
RDB和AOF優(yōu)缺點
RDB的優(yōu)點?
RDB是一個非常緊湊(compact)的文件,體積小,網(wǎng)絡(luò)傳輸快,它保存了Redis在某個時間點上的數(shù)據(jù)集。這種文件非常適合用于進(jìn)行備份,恢復(fù)速度比AOF快很多。當(dāng)然,與AOF相比,RDB最重要的優(yōu)點之一是對性能的影響相對較小。父進(jìn)程在保存RDB文件時唯一要做的就是fork出一個子進(jìn)程,然后這個子進(jìn)程就會處理接下來的所有保存工作,父進(jìn)程無須執(zhí)行任何磁盤 I/O 操作。
RDB的缺點?
RDB文件的致命缺點在于其數(shù)據(jù)快照的持久化方式?jīng)Q定了必然做不到實時持久化,而在數(shù)據(jù)越來越重要的今天,數(shù)據(jù)的大量丟失很多時候是無法接受的,因此AOF持久化成為主流。此外,RDB文件需要滿足特定格式,兼容性差(如老版本的Redis不兼容新版本的RDB文件)。
AOF的優(yōu)點?
與RDB持久化相對應(yīng),AOF的優(yōu)點在于支持秒級持久化、兼容性好。你可以設(shè)置不同的fsync策略,比如無fsync,每秒鐘一次fsync,或者每次執(zhí)行寫入命令時fsync。AOF的默認(rèn)策略為每秒鐘fsync一次,在這種配置下,Redis仍然可以保持良好的性能,并且就算發(fā)生故障停機(jī),也最多只會丟失一秒鐘的數(shù)據(jù)。AOF文件是一個只進(jìn)行追加操作的日志文件(append only log),因此對AOF文件的寫入不需要進(jìn)行seek(查找),即使日志因為某些原因而包含了未寫入完整的命令(比如寫入時磁盤已滿,寫入中途停機(jī),等等), redis-check-aof 工具也可以輕易地修復(fù)這種問題。
Redis可以在AOF文件體積變得過大時,自動地在后臺對AOF進(jìn)行重寫: 重寫后的新AOF文件包含了恢復(fù)當(dāng)前數(shù)據(jù)集所需的最小命令集合。 整個重寫操作是絕對安全的,因為 Redis 在創(chuàng)建新 AOF 文件的過程中,會繼續(xù)將命令追加到現(xiàn)有的AOF文件里面,即使重寫過程中發(fā)生停機(jī),現(xiàn)有的AOF文件也不會丟失。 而一旦新AOF文件創(chuàng)建完畢,Redis就會從舊AOF文件切換到新AOF文件,并開始對新AOF文件進(jìn)行追加操作。AOF文件有序地保存了對數(shù)據(jù)庫執(zhí)行的所有寫入操作,這些寫入操作以Redis協(xié)議的格式保存,因此AOF文件的內(nèi)容非常容易被人讀懂,對文件進(jìn)行分析(parse)也很輕松。導(dǎo)出(export)AOF文件也非常簡單: 舉個例子, 如果你不小心執(zhí)行了FLUSHALL命令,但只要AOF文件未被重寫,那么只要停止服務(wù)器,移除AOF文件末尾的FLUSHALL命令,并重啟 Redis, 就可以將數(shù)據(jù)集恢復(fù)到FLUSHALL執(zhí)行之前的狀態(tài)。
AOF的缺點?
AOF文件的體積通常要大于RDB文件的體積、且恢復(fù)速度慢。對于相同的數(shù)據(jù)集來說,根據(jù)所使用的fsync策略,AOF的速度可能會慢于RDB。在一般情況下,每秒fsync的性能依然非常高,而關(guān)閉fsync可以讓AOF的速度和RDB一樣快。另外,AOF在過去曾經(jīng)發(fā)生過這樣的bug,因為個別命令的原因,導(dǎo)致AOF文件在重新載入時,無法將數(shù)據(jù)集恢復(fù)成保存時的原樣。雖然這種bug在AOF文件中并不常見,但是對比來說,RDB幾乎是不可能出現(xiàn)這種bug的。
RDB和AOF,我應(yīng)該用哪一個?
首先要明白無論是RDB還是AOF,持久化的開啟都是要付出性能方面代價的:對于RDB持久化,一方面是bgsave在進(jìn)行fork操作時Redis主進(jìn)程會阻塞,另一方面,子進(jìn)程向硬盤寫數(shù)據(jù)也會帶來IO壓力。但如果業(yè)務(wù)能容忍幾分鐘到10幾分鐘的數(shù)據(jù)丟失(且不使用備庫),RDB是一個不錯的選擇;不然,就選擇AOF。
對于AOF持久化,向硬盤寫數(shù)據(jù)的頻率大大提高(everysec策略下為秒級),IO壓力更大,甚至可能造成AOF追加阻塞問題(后面會詳細(xì)介紹這種阻塞),此外,AOF文件的重寫與RDB的bgsave類似,會有fork時的阻塞和子進(jìn)程的IO壓力問題。相對來說,由于AOF向硬盤中寫數(shù)據(jù)的頻率更高,因此對Redis主進(jìn)程性能的影響會更大。
在實際生產(chǎn)環(huán)境中,根據(jù)數(shù)據(jù)量、應(yīng)用對數(shù)據(jù)的安全要求、預(yù)算限制等不同情況,會有各種各樣的持久化策略;如完全不使用任何持久化、使用RDB或AOF的一種,或同時開啟RDB和AOF持久化等。此外,持久化的選擇必須與Redis的主從策略一起考慮,因為主從復(fù)制與持久化同樣具有數(shù)據(jù)備份的功能,而且主機(jī)master和從機(jī)slave可以獨立的選擇持久化方案。比如完全關(guān)閉master持久化(包括RDB和AOF),這樣可以讓master的性能達(dá)到最好;而slave可以只開啟AOF。但這種情況下,如果master服務(wù)因為故障宕掉了,如果系統(tǒng)中有自動拉起機(jī)制(即檢測到服務(wù)停止后重啟該服務(wù))將master自動重啟,由于沒有持久化文件,那么master重啟后數(shù)據(jù)是空的,slave同步數(shù)據(jù)也變成了空的,意味著數(shù)據(jù)丟失。所以盡量避免這種情況出現(xiàn)。
RDB和AOF之間的相互作用?
在版本號大于等于2.4的Redis中,BGSAVE執(zhí)行的過程中,不可以執(zhí)行BGREWRITEAOF。反過來說,在BGREWRITEAOF執(zhí)行的過程中,也不可以執(zhí)行BGSAVE。這可以防止兩個Redis后臺進(jìn)程同時對磁盤進(jìn)行大量的I/O操作。
如果BGSAVE正在執(zhí)行,并且用戶顯示地調(diào)用BGREWRITEAOF命令,那么服務(wù)器將向用戶回復(fù)一個OK狀態(tài),并告知用戶,BGREWRITEAOF已經(jīng)被預(yù)定執(zhí)行: 一旦BGSAVE執(zhí)行完畢,BGREWRITEAOF就會正式開始。當(dāng)Redis啟動時,如果RDB持久化和AOF持久化都被打開了,那么程序會優(yōu)先使用AOF文件來恢復(fù)數(shù)據(jù)集,因為AOF文件所保存的數(shù)據(jù)通常是最完整的。
RDB和AOF數(shù)據(jù)導(dǎo)入
這些持久化的數(shù)據(jù)有什么用,當(dāng)然是用于重啟后的數(shù)據(jù)恢復(fù)。Redis是一個內(nèi)存數(shù)據(jù)庫,無論是RDB還是AOF,都只是其保證數(shù)據(jù)恢復(fù)的措施。所以Redis在利用RDB或AOF進(jìn)行恢復(fù)的時候,會讀取RDB或AOF文件,重新加載到內(nèi)存中。相對于MySQL等數(shù)據(jù)庫的啟動時間來說,會長很多,因為MySQL本來是不需要將數(shù)據(jù)加載到內(nèi)存中的。
但是相對來說,MySQL啟動后提供服務(wù)時,其被訪問的熱數(shù)據(jù)也會慢慢加載到內(nèi)存中,通常我們稱之為預(yù)熱,而在預(yù)熱完成前,其性能都不會太高。而Redis的好處是一次性將數(shù)據(jù)加載到內(nèi)存中,一次性預(yù)熱。這樣只要Redis啟動完成,那么其提供服務(wù)的速度都是非??斓?。
而在利用RDB和利用AOF啟動上,其啟動時間有一些差別。RDB的啟動時間會更短,原因有兩個,一是RDB文件中每一條數(shù)據(jù)只有一條記錄,不會像AOF日志那樣可能有一條數(shù)據(jù)的多次操作記錄。所以每條數(shù)據(jù)只需要寫一次就行了。另一個原因是RDB文件的存儲格式和Redis數(shù)據(jù)在內(nèi)存中的編碼格式是一致的,不需要再進(jìn)行數(shù)據(jù)編碼工作。在CPU消耗上要遠(yuǎn)小于AOF日志的加載。
注意:當(dāng)redis啟動時,如果rdb持久化和aof持久化都打開了,那么程序會優(yōu)先使用aof方式來恢復(fù)數(shù)據(jù)集,因為aof方式所保存的數(shù)據(jù)通常是最完整的。如果aof文件丟失了,則啟動之后數(shù)據(jù)庫內(nèi)容為空。
注意:如果想把正在運行的redis數(shù)據(jù)庫,從RDB切換到AOF,建議先使用動態(tài)切換方式,再修改配置文件,重啟數(shù)據(jù)庫。(不能直接修改配置文件,重啟數(shù)據(jù)庫,否則數(shù)據(jù)庫中數(shù)據(jù)就為空了。)
在Redis 2.2或以上版本,可以在不重啟的情況下,從RDB切換到AOF :
為最新的dump.rdb文件創(chuàng)建一個備份,將備份放到一個安全的地方。執(zhí)行以下兩條命令:
127.0.0.1:6379> CONFIG SET dir /apps/redis/data/redis-8836 127.0.0.1:6379> CONFIG SET appendonly yes 127.0.0.1:6379> CONFIG SET save ""
確保命令執(zhí)行之后,數(shù)據(jù)庫的鍵的數(shù)量沒有改變。確保寫命令會被正確地追加到 AOF 文件的末尾。
步驟2是開啟了AOF功能,Redis會阻塞直到初始AOF文件創(chuàng)建完成為止,之后Redis會繼續(xù)處理命令請求,并開始將寫入命令追加到AOF文件末尾。
步驟3用于關(guān)閉RDB功能,這一步是可選的,如果你愿意的話,也可以同時使用RDB和AOF這兩種持久化功能。
看完上述內(nèi)容,你們掌握如何進(jìn)行Redis持久化RDB和AOF的分析的方法了嗎?如果還想學(xué)到更多技能或想了解更多相關(guān)內(nèi)容,歡迎關(guān)注億速云行業(yè)資訊頻道,感謝各位的閱讀!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。