也可以命令行的方式讓redis進行snapshotting:
redis-cli -h ip -p port bgsave保存快照有save和bgsave兩個命令,save操作是在主線程中保存快照的,由於redis是用一個主線程來處理所有client的請求,這種方式會阻塞所有client請求,所以不推薦使用。
快照生成過程大致如下:
redis調用fork,現在有了子進程和父進程;父 進程繼續處理client請求,子進程負責將內存內容寫入到臨時文件。由於os的寫時復制機制(copy on write)父子進程會共享相同的物理頁面,當父進程處理寫請求時os會為父進程要修改的頁面創建副本,而不是寫共享的頁面。所以子進程的地址空間內的數 據是fork時刻整個數據庫的一個快照;當子進程將快照寫入臨時文件完畢後,用臨時文件替換原來的快照文件,然後子進程退出。同時snapshotting也有不足的,因為兩次快照操作之間是有時間間隔的,一旦數據庫出現問題,那麼快照文件中保存的數據並不是全新的,從上次快照文件生成到Redis停機這段時間的數據全部丟掉了。如果業務對數據准確性要求極高的話,就得采用aof持久化機制了。
aof
aof比快照方式有更好的持久化性,是由於在使用aof持久化方式時,redis會將每一個收到的寫命令都通過write函數追加到文件中(默認是appendonly.aof)。當redis重啟時會通過重新執行文件中保存的寫命令來在內存中重建整個數據庫的內容。當然由於os會在內核中緩存write做的修改,所以可能不是立即寫到磁盤上。這樣aof方式的持久化也還是有可能會丟失部分修改。不過我們可以通過配置文件告訴redis我們想要通過fsync函數強制os寫入到磁盤的時機。有三種方式如下(默認是:每秒fsync一次):
appendonly yes //啟用aof持久化方式 # appendfsync always //每次收到寫命令就立即強制寫入磁盤,最慢的,但是保證完全的持久化,不推薦使用 appendfsync everysec //每秒鐘強制寫入磁盤一次,在性能和持久化方面做了很好的折中,推薦 # appendfsync no //完全依賴os,性能最好,持久化沒保證aof的方式也同時帶來了另一個問題。持久化文件會變的越來越大。例如我們調用incrtest命令100次,文件中必須保存全部的100條命令,其實有99條都是多余的。因為要恢復數據庫的狀態其實文件中保存一條set test100就夠了。為了壓縮aof的持久化文件。redis提供了bgrewriteaof命令。收到此命令redis將使用與快照類似的方式將內存中的數據以命令的方式保存到臨時文件中,最後替換原來的文件。bgrewriteaof命令如下:
redis-cli -h ip -p port bgrewriteaofbgrewriteaof命令執行過程如下:
redis調用fork ,現在有父子兩個進程;子進程根據內存中的數據庫快照,往臨時文件中寫入重建數據庫狀態的命令;父進程繼續處理client請求,除了把寫命令寫入到原來的aof文件中。同時把收到的寫命令緩存起來。這樣就能保證如果子進程重寫失敗的話並不會出問題;當子進程把快照內容寫入以命令方式寫到臨時文件中後,子進程發信號通知父進程。然後父進程把緩存的寫命令也寫入到臨時文件;現在父進程可以使用臨時文件替換老的aof文件,並重命名,後面收到的寫命令也開始往新的aof文件中追加。這兩種持久化方式有各自的特點,快照相對性能影響不大,但一旦崩潰,數據量丟失較大,而aof數據安全性較高,但性能影響較大,這就得根據業務特點自行選擇了。
redis的主從復制策略是通過其持久化的rdb文件來實現的,其過程是先dump出rdb文件,將rdb文件全量傳輸給slave,然後再將dump後的操作實時同步到slave中。
要使用主從功能需要在slave端進行簡單的配置:
slaveof master_ip master_port #如果這台機器是台redis slave,可以打開這個設置。 slave-serve-stale-data no #如果slave 無法與master 同步,設置成slave不可讀,方便監控腳本發現題。如果是master--slave(master)--slave這種主從模式的話和master --slave 這種模式是相同的所以配置文件和是和master--slave 一樣進行配置就可以。配置好之後啟動slave端就可以進行主從復制了,主從復制的過程大致如下:
Slave端在配置文件中添加了slaveof指令,於是Slave啟動時讀取配置文件,初始狀態為REDIS_REPL_CONNECT;Slave端在定時任務serverCron(Redis內部的定時器觸發事件)中連接Master,發送sync命令,然後阻塞等待master發送回其內存快照文件(最新版的Redis已經不需要讓Slave阻塞);Master端收到sync命令簡單判斷是否有正在進行的內存快照子進程,沒有則立即開始內存快照,有則等待其結束,當快照完成後會將該文件發送給Slave端;Slave端接收Master發來的內存快照文件,保存到本地,待接收完成後,清空內存表,重新讀取Master發來的內存快照文件,重建整個內存表數據結構,並最終狀態置位為 REDIS_REPL_CONNECTED狀態,Slave狀態機流轉完成;Master端在發送快照文件過程中,接收的任何會改變數據集的命令都會暫時先保存在Slave網絡連接的發送緩存隊列裡(list數據結構),待快照完成後,依次發給Slave,之後收到的命令相同處理,並將狀態置位為 REDIS_REPL_ONLINE。整個復制過程完成,流程如下圖所示:
從以上的復制過程中可以發現,Slave從庫在連接Master主庫時,Master會進行內存快照,然後把整個快照文件發給Slave,也就是沒有象MySQL那樣有復制位置的概念,即無增量復制,如果一個master連接多個slave,就會比較影響master性能了。
具體的備份策略是可以很靈活的,比如可以大致如下:
為了提高master的性能關閉master的持久化機制,即不進行快照也不進行aof,而是在凌晨訪問量低的時候定時的用bgsave命令進行快照,並將快照文件保存到備份服務器上;slave端開啟aof機制,並定時的用bgrewriteaof 進行數據壓縮,將壓縮後的數據文件保存到備份服務器上;定時的檢查master與slave上的數據是否一致;當 master出問題並需要恢復時,如果采用master的備份快照恢復直接將備份的dump.rdb拷貝到相應路徑下重啟即可;如果要從slave端恢 復,需要在slave端執行一次快照,然後將快照文件拷貝到master路徑下然後重啟即可。不過有一點需要注意的是,master重啟時slave端數 據會被沖掉,所以slave端要在master重啟前做好備份。持久化磁盤IO方式及其帶來的問題
有Redis線上運維經驗的人會發現Redis在物理內存使用比較多,但還沒有超過實際物理內存總容量時就會發生不穩定甚至崩潰的問題,有人認為是基於快照方式持久化的fork系統調用造成內存占用加倍而導致的,這種觀點是不准確的,因為 fork調用的copy-on-write機制是基於操作系統頁這個單位的,也就是只有有寫入的髒頁會被復制,但是一般的系統不會在短時間內所有的頁都發生了寫入而導致復制,那麼是什麼原因導致Redis崩潰的呢?
答案是Redis的持久化使用了BufferIO造成的,所謂Buffer IO是指Redis對持久化文件的寫入和讀取操作都會使用物理內存的Page Cache,而大多數數據庫系統會使用DirectIO來繞過這層PageCache並自行維護一個數據的Cache,而當Redis的持久化文件過大(尤其是快照文件),並對其進行讀寫時,磁盤文件中的數據都會被加載到物理內存中作為操作系統對該文件的一層Cache,而這層Cache的數據與Redis內存中管理的數據實際是重復存儲的,雖然內核在物理內存緊張時會做 PageCache的剔除工作,但內核可能認為某塊PageCache更重要,而讓你的進程開始Swap,這時你的系統就會開始出現不穩定或者崩潰了。經驗是當你的Redis物理內存使用超過內存總容量的3/5時就會開始比較危險了。
1、 快照的方式持久化到磁盤
自動持久化規則配置
save 900 1
save 300 10
save 60 10000
上面的配置規則意思如下:
# In the example below the behaviour will be to save:
# after 900 sec (15 min) if at least 1 key changed
# after 300 sec (5 min) if at least 10 keys changed
# after 60 sec if at least 10000 keys changed
redis也可以關閉自動持久化,注釋掉這些save配置,或者save“”
如果後台保存到磁盤發生錯誤,將停止寫操作.
stop-writes-on-bgsave-erroryes
使用LZF壓縮rdb文件,這會耗CPU,但是可以減少磁盤占用.
rdbcompression yes
保存rdb和加載rdb文件的時候檢驗,可以防止錯誤,但是要付出約10%的性能,可以關閉他,提高性能。
rdbchecksum yes
導出的rdb文件名
dbfilename dump.rdb
設置工作目錄, rdb文件會寫到該目錄,append only file也會存儲在該目錄下.
dir ./
Redis自動快照保存到磁盤或者調用bgsave,是後台進程完成的,其他客戶端仍然和可以讀寫redis服務器,後台保存快照到磁盤會占用大量內存。調用save保存內存中的數據到磁盤,將阻塞客戶端請求,直到保存完畢。
調用shutdown命令,Redis服務器會先調用save,所有數據持久化到磁盤之後才會真正退出。
對於數據丟失的問題:
如果服務器crash,從上一次快照之後的數據將全部丟失。所以在設置保存規則的時候,要根據實際業務設置允許的范圍。
如果對於數據敏感的業務,在程序中要使用恰當的日志,在服務器crash之後,通過日志恢復數據。
2、 Append-only file 的方式持久化
另外一種方式為遞增的方式,將會引起數據變化的操作,持久化到文件中, 重啟redis的時候,通過操作命令,恢復數據.
每次執行寫操作命令之後,都會將數據寫到server.aofbuf中。
# appendfsync always
appendfsync everysec
# appendfsync no
當配置為always的時候,每次server.aofbuf中的數據寫入到文件之後,才會返回給客戶端,這樣可以保證數據不丟,但是頻繁的IO操作,會降低性能。
everysec每秒寫一次,這可能會丟失一秒內的操作。
aof最大的問題就是隨著時間appendfile會變的很大,所以我們需要bgrewriteaof命令重新整理文件,只保留最新的kv數據。