上一篇文章: Python--Redis實戰:第四章:數據安全與性能保障:第1節:持久化選項
下一篇文章: Python--Redis實戰:第四章:數據安全與性能保障:第3節:AOF持久化
Redis能夠經過建立快照來得到存儲在內存裏面的數據在某個時間點上的副本。在建立快照以後,用戶能夠對快照進行備份,能夠將快照複製到其它服務器從而建立具備相同數據的服務器副本,還能夠將快照留在原地以便重啓服務器時使用。redis
根據配置,快照將被寫入dbfilename選項指定的文件裏面,並存儲在dir選項指定的路徑上面。若是在新的快照文件建立成功以前,Redis、系統或者硬件上三者之中的任意一個崩潰了,那麼Redis將丟失最近一次建立快照以後寫入的全部數據。segmentfault
舉個例子,假設Redis目前在內存裏面存儲了10GB的數據,上一個快照是在下午2:35開始建立的,而且已經建立成功。下午3:06時,Redis又開始建立新的快照,而且在下午3:08快照文件建立完畢以前,有35個鍵進行了更新。若是在下午3:06至3:08期間,系統發生崩潰,致使Redis沒法完成新快照的建立工做,那麼Redis將丟失下午2:35以後寫入的全部數據。安全
建立快照的辦法有如下幾種:服務器
fork:當一個進程建立子進程的時候,底層的操做系統會建立該進程的一個副本。在Unix和類Unix系統上面,建立子進程的操做會進行以下優化:在剛開始的時候,父進程共享相同的內存,直到父進程或者子進程對內存進行了寫入以後,寫入內存的共享纔會結束。
在只使用快照持久化來保存數據時,必定要記住:若是系統真的發生崩潰,用戶將丟失最近一次生成快照以後更改的全部數據。所以,快照持久化只適用於那些丟失一部分數據也不會形成問題的應用程序,而不能接受這種數據損失的應用程序則能夠考慮使用AOF持久化。接下來將展現幾個使用快照持久化的場景,讀者能夠從中學習到如何經過修改配置來得到資金想要的快照持久化的行爲。閉包
在我的開發服務器上面,我主要考慮的是儘量地下降快照持久化帶來的資源消耗。基於這個緣由以及對本身硬件的信任,我只設置了save 900 1這一條規則。其中save 選項告知Redis,它應該根據這個選項提供的兩個值來執行bgsave操做。在這個規則設置下,若是服務器距離上次成功生成快照超過了900秒(15分鐘),而且在運行期間執行了至少一次寫入操做,那麼Redis久自動開始一次新的bgsave操做。函數
若是你打算在生產服務器中使用快照持久化並存儲大量數據,那麼你的開發服務器最好可以運行在與生產服務器相同或者類似的硬件上面,並在這兩個服務器上使用相同的save選項、存儲類似的數據集並處理類似的負載量。把開發環境設置得儘可能貼近生產環境,有助於判斷快照是否生成的過於頻繁或者稀少。過於頻繁會浪費資源,過於稀少則帶有丟失大量數據的隱患。性能
在對日誌文件進行聚合計算或者對頁面瀏覽量進行分析的時候,咱們須要惟一考慮的就是:若是Redis由於崩潰而未能成功建立新的快照,那麼咱們可以承受丟失多長時間之內產生的新數據。若是丟失一個小時以內產生的數據是能夠接受的,那麼可使用配置值save 3600 1(3600秒爲1小時)。在決定好了持久化配置值以後,另外一個須要解決的問題就是如何恢復由於故障而被終端的日誌處理操做。學習
在進行數據恢復時,首先要作的就是弄清楚咱們丟失了哪些數據。爲了弄明白這一點,咱們須要在處理日誌的同時記錄被處理日誌的相關信息。大數據
下面代碼展現了一個用於處理新日誌的函數,該函數有3個參數,他們分別是:一個Redis連接;一個存儲日誌文件的路徑;待處理日誌文件中各個行(line)的回調函數。這個函數能夠在處理日誌文件的同時,記錄被處理的日誌文件的名字以及偏移量。優化
import os import redis # 導入redis包包 #日誌處理函數接收的其中以惡搞參數爲回調函數 #這個回調函數接收一個Redis鏈接和一個日誌行做爲參數, #並經過調用流水線呢對象的方法來執行Redis命令 def process_log(conn,path,callback): #獲取文件當前的處理進度 current_file,offset=conn.mget('progress.file','progress.position') pipe=conn.pipeline() #經過使用閉包(closur)來減小重複代碼 def update_progress(): pipe.mset({'progress.file':fname,'progress:position':offset}) #這個語句負責執行實際的日誌更細操做,並將日誌文件的名字和目前的處理器進度記錄到Redis裏面 pipe.execute() #有序的遍歷各個日誌文件 for fname in sorted(os.listdir(path)): #略過全部已處理的日誌文件 if fname <current_file: continue inp=open(os.path.join(path,fname),'rb') #在接着處理一個由於系統崩潰而未能完成處理的日誌文件時,略過已處理的內容 if fname==current_file: inp.seek(int(offset,10)) else: offset=0 current_file=None #枚舉函數遍歷一個由文件行足哼的序列,並返回任意多個二元組 #每一個二元祖包含了行號lno和行數據line,其中行號從0開始 for lno,line in enumerate(inp): #處理日誌 callback(pipe,line) #更細已處理內容的偏移量 offset+=int(offset)+len(line) #每當處理完1000個日誌行或者處理完 整個日誌文件的時候,都更新一次文件的處理進度 if not (lno+1) %1000: update_progress() update_progress() inp.close()
經過將日誌的處理進度記錄到Redis裏面,程序能夠在系統崩潰以後,根據進度記錄繼續執行以前未完成的處理工做。而經過事務流水線,程序保證日誌的處理結果和處理進度老是會同時被記錄到快照文件裏面。
在Redis存儲的數據量只有幾個GB的時候,使用快照來保存數據是沒有問題的。Redis會建立子進程並將數據保存到硬盤裏面,生成快照須要的時間比你讀這句話的時間還要短。隨着Redis佔用的內存愈來愈多,bgsave在建立子進程時耗費的時間愈來愈多。若是Redis的內存佔用量達到數十個GB,而且剩餘的空閒內存並很少,或者Redis運行在虛擬機上面,那麼執行bgsave可能會致使系統長時間地停頓,也可能引起系統大量地使用虛擬內存,從而致使Redis的性能下降至沒法使用的程度。
執行bgsave而致使的挺短期有多長取決於Redis所在的系統:對於真實的硬件,vmware虛擬機或者KVM虛擬機來講,Redis進行每佔用一個GB的內存,建立該進程的子進程所需的時間就要增長10~20毫秒,而對於Xen虛擬機來講,根究配置的不一樣,Redis進程每佔用一個GB的內存,建立該進程的子進程所需的時間就要增長200~300毫秒。所以,若是咱們的Redis進程佔用了20GB的內存,那麼在標準硬件上運行bgsave所建立的子進程將致使Reeis停頓200~400毫秒,若是咱們使用的是Xen虛擬機(亞馬遜EC2和其餘幾個雲計算供應商都使用這種虛擬機),那麼相同的建立子進程操做將致使Redis停頓4~6秒。用戶必須考慮本身的應用程序可否接受這種停頓。
爲了防止Redeis由於建立子進程而出現停頓,咱們能夠考慮關閉自動保存呢,轉而經過手動發送bgsave或者save來進行持久化。手動發送bgsave同樣會引發停頓,惟一不一樣的是用戶能夠控制停頓出現的時間。另外一方面,雖然save會一直阻塞Redis知道快照生產完畢,可是由於它不須要建立子進程,因此就不會像bgsave同樣由於建立子進程而致使Redis停頓;而且由於沒有子進程在爭搶資源,因此save建立快照的速度比bgsave建立快照的速度要快一些。
根據我的經驗,在一臺擁有68GB內存的Xen虛擬機上面,對一個佔用50GB內存的Redis服務器執行bgsave命令的話,光是建立子進程就須要花費15秒以上,而生成快照則須要花費15~20分鐘;但使用save值須要3~5分鐘就能夠完成快照的生成工做。由於的應用程序只須要天天生成一次快照,因此我寫了一個腳本,讓它在天天凌晨3點中止全部客戶端對Redis的訪問,調用save命令並等待該命令執行完畢,以後備份剛剛生成的快照文件,並通知客戶端繼續執行操做。
若是用戶可以妥善地處理快照持久化可能會帶來的大量數據丟失,那麼快照持久化對用戶來講將使一個不錯的選擇,但對於不少用於程序來講,丟失15分鐘、1小時甚至更長時間的數據都是不可接受的,在這種狀況狀況下,咱們可使用AOF持久化來將存儲在內存裏面的數據儘快的保存到硬盤裏面。
上一篇文章: Python--Redis實戰:第四章:數據安全與性能保障:第1節:持久化選項
下一篇文章: Python--Redis實戰:第四章:數據安全與性能保障:第3節:AOF持久化