Redis數據持久化

持久化選項

Redis提供了兩種不一樣的持久化方法來將數據存儲到硬盤裏面。一種方法叫快照(snapshotting),它能夠將存在於某一時刻的全部數據都寫入硬盤裏面。另外一種方法叫只追加文件(append-onlyfile,AOF),它會在執行寫命令時,將被執行的寫命令複製到硬盤裏面。這兩種持久化方法既能夠同時使用,又能夠單獨使用,在某些狀況下甚至能夠兩種方法都不使用,具體選擇哪一種持久化方法須要根據用戶的數據以及應用來決定。數據庫

將內存中的數據存儲到硬盤的一個主要緣由是爲了在以後重用數據,或者是爲了防止系統故障而將數據備份到一個遠程位置。另外,存儲在Redis裏面的數據有多是通過長時間計算得出的,或者有程序正在使用Redis存儲的數據進行計算,因此用戶會但願本身能夠將這些數據存儲起來以便以後使用,這樣就沒必要再從新計算了。對於一些Redis應用來講,「計算」可能只是簡單地將另外一個數據庫的數據複製到Redis裏面,但對於另一些Redis應用來講,Redis存儲的數據多是根據數十億行日誌進行聚合分析得出的結果。編程

兩組不一樣的配置選項控制着Redis將數據寫入硬盤裏面的方式,代碼清單4-1展現了這些配置選項以及它們的示例配置值。安全

代碼清單4-1最開頭的幾個選項和快照持久化有關,好比:如何命名硬盤上的快照文件、多久執行一次自動快照操做、是否對快照文件進行壓縮,以及在建立快照失敗後是否仍然繼續執行寫命令。代碼清單的第二組選項用於配置AOF子系統(subsystem):這些選項告訴Redis是否使用AOF持久化、多久纔將寫入的內容同步到硬盤、在對AOF進行壓縮(compaction)的時候可否執行同步操做,以及多久執行一次AOF壓縮。服務器

快照持久化

Redis 能夠經過建立快照來得到存儲在內存裏面的數據在某個時間點上的副本。在建立快照 以後,用戶能夠對快照進行備份,能夠將快照複製到其餘服務器從而建立具備相同數據的服務器副本,還能夠將快照留在原地以便重啓服務器時使用。app

根據配置,快照將被寫入dbfilename選項指定的文件裏面,並儲存在dir選項指定的路徑上面。若是在新的快照文件建立完畢以前,Redis、系統或者硬件這三者之中的任意一個崩潰了,那麼Redis將丟失最近一次建立快照以後寫入的全部數據。編程語言

舉個例子,假設Redis目前在內存裏面存儲了10GB的數據,上一個快照是在下午2:35開始建立的,而且已經建立成功。下午3:06時,Redis又開始建立新的快照,而且在下午3:08快照文件建立完畢以前,有35個鍵進行了更新。若是在下午3:06至下午3:08期間,系統發生崩潰,致使Redis沒法完成新快照的建立工做,那麼Redis將丟失下午2:35以後寫入的全部數據。另外一方面,若是系統剛好在新的快照文件建立完畢以後崩潰,那麼Redis將只丟失35個鍵的更新數據。性能

建立快照的辦法有如下幾種。學習

  • 客戶端能夠經過向Redis發送BGSAVE命令來建立一個快照。對於支持BGSAVE命令的平臺來講(基本上全部平臺都支持,除了Windows平臺),Redis會調用fork來建立一個子進程,而後子進程負責將快照寫入硬盤,而父進程則繼續處理命令請求。
  • 客戶端還能夠經過向Redis發送SAVE命令來建立一個快照,接到SAVE命令的Redis服務器在快照建立完畢以前將再也不響應任何其餘命令。SAVE命令並不經常使用,咱們一般只會在沒有足夠內存去執行BGSAVE命令的狀況下,又或者即便等待持久化操做執行完畢也無所謂的狀況下,纔會使用這個命令。
      • 若是用戶設置了save配置選項,好比save 60 10000,那麼從Redis最近一次建立快照以後開始算起,當「60秒以內有10000次寫入」這個條件被知足時,Redis就會自動觸發BGSAVE命令。若是用戶設置了多個save配置選項,那麼當任意一個save配置選項所設置的條件被知足時,Redis就會觸發一次BGSAVE命令。
  • 當Redis經過SHUTDOWN命令接收到關閉服務器的請求時,或者接收到標準TERM信號時,會執行一個SAVE命令,阻塞全部客戶端,再也不執行客戶端發送的任何命令,並在SAVE命令執行完畢以後關閉服務器。
  • 當一個Redis服務器鏈接另外一個Redis服務器,並向對方發送SYNC命令來開始一次複製操做的時候,若是主服務器目前沒有在執行BGSAVE操做,或者主服務器並不是剛剛執行完BGSAVE操做,那麼主服務器就會執行BGSAVE命令。

在只使用快照持久化來保存數據時,必定要記住:若是系統真的發生崩潰,用戶將丟失最近一次生成快照以後更改的全部數據。所以,快照持久化只適用於那些即便丟失一部分數據也不會形成問題的應用程序,而不能接受這種數據損失的應用程序則能夠考慮使用AOF持久化。大數據

快照持久化的場景

接下來將展現幾個使用快照持久化的場景,你們能夠從中學習到如何經過修改配置來得到本身想要的快照持久化行爲。 雲計算

1.我的開發

在我的開發服務器上面,主要考慮的是儘量地下降快照持久化帶來的資源消耗。基於這個緣由以及對本身硬件的信任,我只設置了save 90 01這一條規則。其中save選項告知Redis,它應該根據這個選項提供的兩個值來執行BGSAVE操做。在這個規則設置下,若是服務器距離上次成功生成快照已經超過了900秒(也就是15分鐘),而且在此期間執行了至少一次寫入操做,那麼Redis就會自動開始一次新的BGSAVE操做。

若是你打算在生產服務器中使用快照持久化並存儲大量數據,那麼你的開發服務器最好可以運行在與生產服務器相同或者類似的硬件上面,並在這兩個服務器上使用相同的save選項、存儲類似的數據集並處理相近的負載量。把開發環境設置得儘可能貼近生產環境,有助於判斷快照是否生成得過於頻繁或者過於稀少(過於頻繁會浪費資源,而過於稀少則帶有丟失大量數據的隱患)。

2.大數據

當Redis存儲的數據量只有幾個GB的時候,使用快照來保存數據是沒有問題的。Redis會建立子進程並將數據保存到硬盤裏面,生成快照所需的時間比你讀這句話所需的時間還要短。但隨着Redis佔用的內存愈來愈多,BGSAVE在建立子進程時耗費的時間也會愈來愈多。若是Redis的內存佔用量達到幾十個GB,而且剩餘的空閒內存並很少,或者Redis運行在虛擬機(virtual machine)上面,那麼執行BGSAVE可能會致使系統長時間地停頓,也可能引起系統大量地使用虛擬內存(virtual memory),從而致使Redis的性能下降至沒法使用的程度。

執行BGSAVE而致使的停頓時間有多長取決於Redis所在的系統:對於真實的硬件、VMWare虛擬機或者KVM虛擬機來講,Redis進程每佔用一個GB的內存,建立該進程的子進程所需的時間就要增長10~20毫秒;而對於Xen虛擬機來講,根據配置的不一樣,Redis進程每佔用一個GB的內存,建立該進程的子進程所需的時間就要增長200~300毫秒。所以,若是咱們的Redis進程佔用了20GB的內存,那麼在標準硬件上運行BGSAVE所建立的子進程將致使Redis停頓200~400毫秒;若是咱們使用的是Xen虛擬機(亞馬遜EC2和其餘幾個雲計算供應商都使用這種虛擬機),那麼相同的建立子進程操做將致使Redis停頓4~6秒。用戶必須考慮本身的應用程序可否接受這種停頓。

爲了防止Redis由於建立子進程而出現停頓,咱們能夠考慮關閉自動保存,轉而經過手動發送BGSAVE或者SAVE來進行持久化。手動發送BGSAVE同樣會引發停頓,惟一不一樣的是用戶能夠經過手動發送BGSAVE命令來控制停頓出現的時間。另外一方面,雖然SAVE會一直阻塞Redis直到快照生成完畢,可是由於它不須要建立子進程,因此就不會像BGSAVE同樣由於建立子進程而致使Redis停頓;而且由於沒有子進程在爭搶資源,因此SAVE建立快照的速度會比BGSAVE建立快照的速度要來得更快一些。

根據我的經驗,在一臺擁有68GB內存的Xen虛擬機上面,對一個佔用50GB內存的Redis服務器執行BGSAVE命令的話,光是建立子進程就須要花費15秒以上,而生成快照則須要花費15~20分鐘;但使用SAVE只須要3~5分鐘就能夠完成快照的生成工做。由於個人應用程序只須要天天生成一次快照,因此我寫了一個腳本,讓它在天天凌晨3點中止全部客戶端對Redis的訪問,調用SAVE命令並等待該命令執行完畢,以後備份剛剛生成的快照文件,並通知客戶端繼續執行操做。

若是用戶可以妥善地處理快照持久化可能會帶來的大量數據丟失,那麼快照持久化對用戶來講將是一個不錯的選擇,但對於不少應用程序來講,丟失15分鐘、1小時甚至更長時間的數據都是不可接受的,在這種狀況下,咱們可使用AOF持久化來將存儲在內存裏面的數據儘快地保存到硬盤裏面。

AOF持久化

簡單來講,AOF持久化會將被執行的寫命令寫到AOF文件的末尾,以此來記錄數據發生的變化。所以,Redis只要從頭至尾從新執行一次AOF文件包含的全部寫命令,就能夠恢復AOF文件所記錄的數據集。AOF持久化能夠經過設置代碼清單4-1所示的appendonly yes配置選項來打開。表4-1展現了appendfsync配置選項對AOF文件的同步頻率的影響。

文件同步  在向硬盤寫入文件時,至少會發生3件事。當調用file.write()方法(或者其餘編程語言裏面的相似操做)對文件進行寫入時,寫入的內容首先會被存儲到緩衝區,而後操做系統會在未來的某個時候將緩衝區存儲的內容寫入硬盤,而數據只有在被寫入硬盤以後,纔算是真正地保存到了硬盤裏面。用戶能夠經過調用file.flush()方法來請求操做系統儘快地將緩衝區存儲的數據寫入硬盤裏,但具體什麼時候執行寫入操做仍然由操做系統決定。除此以外,用戶還能夠命令操做系統將文件同步(sync)到硬盤,同步操做會一直阻塞直到指定的文件被寫入硬盤爲止。當同步操做執行完畢以後,即便系統出現故障也不會對被同步的文件形成任何影響。

若是用戶使用appendfsync always選項的話,那麼每一個Redis寫命令都會被寫入硬盤,從而將發生系統崩潰時出現的數據丟失減到最少。不過遺憾的是,由於這種同步策略須要對硬盤進行大量寫入,因此Redis處理命令的速度會受到硬盤性能的限制:轉盤式硬盤(spinning disk)在這種同步頻率下每秒只能處理大約200個寫命令,而固態硬盤(solid-state drive,SSD)每秒大概也只能處理幾萬個寫命令。

警告:固態硬盤和appendfsync always使用:固態硬盤的用戶請謹慎使用appendfsync always選項,由於這個選項讓Redis每次只寫入一個命令,而不是像其餘appendfsync選項那樣一次寫入多個命令,這種不斷地寫入少許數據的作法有可能會引起嚴重的寫入放大(write amplification)問題,在某些狀況下甚至會將固態硬盤的壽命從原來的幾年下降爲幾個月。

爲了兼顧數據安全和寫入性能,用戶能夠考慮使用appendfsynceverysec選項,讓Redis以每秒一次的頻率對AOF文件進行同步。Redis每秒同步一次AOF文件時的性能和不使用任何持久化特性時的性能相差無幾,而經過每秒同步一次AOF文件,Redis能夠保證,即便出現系統崩潰,用戶也最多隻會丟失一秒以內產生的數據。當硬盤忙於執行寫入操做的時候,Redis還會優雅地放慢本身的速度以便適應硬盤的最大寫入速度。

最後,若是用戶使用appendfsync no選項,那麼Redis將不對AOF文件執行任何顯式的同步操做,而是由操做系統來決定應該在什麼時候對AOF文件進行同步。這個選項在通常狀況下不會對Redis的性能帶來影響,但系統崩潰將致使使用這種選項的Redis服務器丟失不定數量的數據。另外,若是用戶的硬盤處理寫入操做的速度不夠快的話,那麼當緩衝區被等待寫入硬盤的數據填滿時,Redis的寫入操做將被阻塞,並致使Redis處理命令請求的速度變慢。由於這個緣由,通常來講並不推薦使用appendfsync no選項,在這裏介紹它只是爲了完整列舉appendfsync選項可用的3個值。  

雖然AOF持久化很是靈活地提供了多種不一樣的選項來知足不一樣應用程序對數據安全的不一樣要求,但AOF持久化也有缺陷—那就是AOF文件的體積大小。

重寫/壓縮AOF文件

在閱讀了上一節對AOF持久化的介紹以後,讀者可能會感到疑惑:AOF持久化既能夠將丟失數據的時間窗口下降至1秒(甚至不丟失任何數據),又能夠在極短的時間內完成按期的持久化操做,那麼咱們有什麼理由不使用AOF持久化呢?可是這個問題實際上並無那麼簡單,由於Redis會不斷地將被執行的寫命令記錄到AOF文件裏面,因此隨着Redis不斷運行,AOF文件的體積也會不斷增加,在極端狀況下,體積不斷增大的AOF文件甚至可能會用完硬盤的全部可用空間。還有另外一個問題就是,由於Redis在重啓以後須要經過從新執行AOF文件記錄的全部寫命令來還原數據集,因此若是AOF文件的體積很是大,那麼還原操做執行的時間就可能會很是長。

爲了解決AOF文件體積不斷增大的問題,用戶能夠向Redis發送BGREWRITEAOF命令,這個命令會經過移除AOF文件中的冗餘命令來重寫(rewrite)AOF文件,使AOF文件的體積變得儘量地小。BGREWRITEAOF的工做原理和BGSAVE建立快照的工做原理很是類似:Redis會建立一個子進程,而後由子進程負責對AOF文件進行重寫。由於AOF文件重寫也須要用到子進程,因此快照持久化由於建立子進程而致使的性能問題和內存佔用問題,在AOF持久化中也一樣存在。更糟糕的是,若是不加以控制的話,AOF文件的體積可能會比快照文件的體積大好幾倍,在進行AOF重寫並刪除舊AOF文件的時候,刪除一個體積達到數十GB大的舊AOF文件可能會致使操做系統掛起(hang)數秒。

跟快照持久化能夠經過設置save選項來自動執行BGSAVE同樣,AOF持久化也能夠經過設置auto-aof-rewrite-percentage選項和auto-aof-rewrite-min-size選項來自動執行BGREWRITEAOF。舉個例子,假設用戶對Redis設置了配置選項auto-aof-rewrite-percentage 100和auto-aof-rewrite-min-size 64mb,而且啓用了AOF持久化,那麼當AOF文件的體積大於64MB,而且AOF文件的體積比上一次重寫以後的體積大了至少一倍(100%)的時候,Redis將執行BGREWRITEAOF命令。若是AOF重寫執行得過於頻繁的話,用戶能夠考慮將auto-aof-rewrite-percentage選項的值設置爲100以上,這種作法可讓Redis在AOF文件的體積變得更大以後才執行重寫操做,不過也會讓Redis在啓動時還原數據集所需的時間變得更長。

不管是使用AOF持久化仍是快照持久化,將數據持久化到硬盤上都是很是有必要的,但除了進行持久化以外,用戶還必須對持久化所得的文件進行備份(最好是備份到多個不一樣的地方),這樣才能儘可能避免數據丟失事故發生。若是條件容許的話,最好能將快照文件和最新重寫的AOF文件備份到不一樣的服務器上面。

經過使用AOF持久化或者快照持久化,用戶能夠在系統重啓或者崩潰的狀況下仍然保留數據。隨着負載量的上升,或者數據的完整性變得愈來愈重要時,用戶可能須要使用複製特性。

參考資料

黃健宏:<Redis實戰>

相關文章
相關標籤/搜索