Redis持久化
- 首先是一張整理好的思維導圖
Redis持久化有兩種方式,rdb和aof:
- rdb會保存一個時間點的數據快照
- aof會記錄每一個服務器收到的寫操作。在服務器啓動時,這些記錄的操作會逐行從aof文件中執行,從而重建原先的數據集。寫操作命令記錄的格式與Redis協議一致,以追加的方式進行保存
- Redis持久化可以禁用
- Redis兩種持久化方式可以同時存在,重啓redis時,aof優先用於重建
RDB
工作原理
- redis調用fork(),產生一個子進程
- 子進程把數據寫到臨時的rdb文件中
- 當子進程寫完新的rdb文件後,把舊的rdb文件替換掉
優點
- rdb快照是一個簡潔的單文件,保存了某個時間點的redis數據集
- rdb適合做災備,單個rdb文件能很方便的進行傳輸
- 性能好。因爲進行持久化時,主進程會fork一個子進程出來,然後把持久化的工作交給子進程,自己不會有相關的IO操作
- 在數據量較大的情況下,rdb啓動得更快
缺點
- 很容易造成數據的丟失,假設每5min保存一次,出現意外的話,我們可能丟失4min59s的數據
- rdb使用fork產生子進程進行數據的持久化,如果數據比較大的話可能會花費點時間,造成redis停止服務幾毫秒,如果數據量很大且cpu性能不是很好的時候,停止服務的時間甚至會到1s
rdb的啓用和禁用
使Redis如果在每N秒後數據發生了M次改變就自動保存快照文件。
# 格式爲:save <seconds> <changes>
# 可以設置多個。
save 900 1 #900秒後至少1個key有變動
save 300 10 #300秒後至少10個key有變動
save 60 10000 #60秒後至少10000個key有變動
禁用快照保存功能
save ""
其他的自動觸發rdb的方式
- 從節點全量複製時,主節點發送rdb給從節點完成複製操作,主節點會觸發bgsave
- 執行debug reload時
- 執行shutdown時,如果沒有開啓aof,也會觸發
如果rdb時發生錯誤
默認情況下,如果redis在後臺rdb時失敗,Redis則會停止接受更新操作,這樣會讓用戶瞭解到數據沒有被正確的存儲到磁盤上。否則沒人會注意到這個問題,可能會造成災難。 如果後臺存儲(持久化)操作進程再次工作,Redis會自動允許更新操作。然而,如果你已經恰當的配置了對Redis服務器的監視和備份,你也許想關掉這項功能。如此一來即使後臺保存操作出錯,redis也仍然可以繼續像平常一樣工作。
stop-writes-on-bgsave-error yes
數據壓縮配置
默認redis會採用lzf(一種壓縮算法)對數據進行壓縮,禁用掉可以節省cpu
rdbcompression yes
數據校驗配置
從版本5的RDB的開始,一個CRC64
的校驗碼會放在文件的末尾。這樣更能保證文件的完整性,但是在保存或者加載文件時會損失一定的性能(大概10%)。如果想追求更高的性能,可以把它禁用掉,這樣文件在寫入校驗碼時會用0
替代,加載的時候看到0
就會直接跳過校驗。
rdbchecksum yes
手動生成快照
有兩個命令
SAVE
SAVE會使用同步的方式生成RDB快照文件,這意味着在這個過程中會阻塞所有其他客戶端的請求,直到持久化完成。因此不建議在生產環境使用,除非因爲某種原因需要去阻止redis使用子進程進行後臺生成快照。
BGSAVE
BGSAVE命令該觸發方式會fork一個子進程,由子進程負責持久化過程,因此阻塞只會發生在fork子進程的時候。而我們可以使用LASTSAVE命令查看操作的結果。
127.0.0.1:6379> BGSAVE
Background saving started
127.0.0.1:6379> LASTSAVE
(integer) 1433936394
配置文件裏禁用了快照生成功能不影響SAVE和BGSAVE命令的效果
BGSAVE的原理
這裏注意的是fork操作會阻塞,導致Redis讀寫性能下降。我們可以控制單個Redis實例的最大內存,來儘可能降低Redis在fork時的時間消耗。以及上面提到的自動觸發的頻率減少fork次數,或者手動觸發,根據自己的機制來完成持久化。
關於fork操作
同步操作 ,fork 是一個同步操作
- 當執行一個 bgsave 或 bgrewriteaof 首先會執行一個 fork 操作,它只是做一個內存頁的拷貝,並不是拷貝所有內存,所以他的速度是非常快的。
- 當 fork操作比較慢或卡在某一個點,這時它會阻塞redis主線程
與內存量息息相關:內存越大,消耗越長(與機器類型有關)
info:latest_fork_usec – 可以查看 fork 的執行時間
改善fork
優先使用物理機或者高效支持fork操作的虛擬化技術
控制Redis 實例最大可用內存:maxmemory
合理配置Linux內存分配策略:vm.overcommit_memory = 1
默認是0,當發現沒有足夠內存做內存分配時,就會不去分配。對於fork講,會造成fork阻塞。
降低fork頻率:例如放寬AOF重寫自動觸發機制,不必要的全量複製
—— 常見的持久化開發運維問題
總之就是數據量越大fork卡頓的時間就會越長
AOF
由於rdb容易丟失最新的數據,所以aof提供了一種更可靠的持久化方式。每當redis接受到修改命令時,就會把命令追加到aof文件裏去,當重啓redis服務時,aof裏的命令會被重新執行一次,重建數據。
工作過程
- 命令傳播:Redis 將執行完的命令、命令的參數、命令的參數個數等信息發送到 AOF 程序中。
- 緩存追加:AOF 程序根據接收到的命令數據,將命令轉換爲網絡通訊協議的格式,然後將協議內容追加到服務器的 AOF 緩存中。
- 文件寫入和保存:AOF 緩存中的內容被寫入到 AOF 文件末尾。如果設定的 AOF 保存條件被滿足的話,
fsync
函數或者fdatasync
函數會被調用,將寫入的內容真正地保存到磁盤中。
優點
- 更可靠。有三種模式不進行fsync(
fsync是指把緩存中的寫指令記錄到磁盤中
) ,每秒一次fsync,每次修改進行fsync。一般用每秒一次的,意味着你最多丟失一秒鐘的數據。 - aof日誌文件是一個純追加的文件。不會出現日誌損壞問題,海域redis-check-aof可以進行修復文件
- aof文件太大時,redis支持後臺自動重寫。重寫是在一個新的文件上進行,同時redis繼續往舊的aof文件中追加數據。新文件上會寫入能重建當前數據集的最小操作命令集合。重寫完畢後,redis會將新的aof文件替換掉舊的aof,然後把數據開始寫到新文件上。
- AOF把操作命令以簡單易懂的格式一條接一條的保存在文件裏,很容易導出來用於恢復數據。例如我們不小心用
FLUSHALL
命令把所有數據刷掉了,只要文件沒有被重寫,我們可以把服務停掉,把最後那條命令刪掉,然後重啓服務,這樣就能把被刷掉的數據恢復回來。
缺點
- aof文件較大於rdb
- 每秒一次fsync,每次修改進行fsync策略下,速度慢於rdb
相關的配置
appendony yes ##啓用aof
dir ./ ##文件存放目錄,與rdb共用
appendfilename ##默認文件名
## fsync策略(三種)
appendfsync always ## 每當有新命令追加到AOF的時候調用fsync。速度最慢,但是最安全。
appendfsync everysec ## 每秒fsync一次。速度快(2.4版本跟快照方式速度差不多),安全性不錯(最多丟失1秒的數據)。
appendfsync no ## 從不fsync,交由系統去處理。這個方式速度最快,但是安全性一般。
關於appendfsync no的說明
在這種模式下,在寫入內存的aof_buf後,redis僅僅將aof_buf寫入到aof文件中,但不會調用fsync
函數或者 fdatasync
函數將寫入的內容真正地保存到磁盤中。
在這種模式下,真正的保存只會在一下任意一種情況中被執行:
- redis被關閉
- aof功能被關閉
- 系統的寫緩存被刷新(可能是緩存已經被寫滿,或者定期保存操作被執行)
這三種情況都會引起redis主進程阻塞,always的保存功能是由redis主進程執行的,所以也會阻塞。everysec中,保存功能是由子進程進行的,所以不會引起主進程阻塞。
其日誌重寫原理
當寫操作命令不斷增加時,aof文件會不斷增大,有一種aof文件優化思路是“只需要重建能夠恢復當前狀態的aof文件即可,也就是說如果有一個遞增的計數器到100次,新的aof文件只需要記錄其最後的值即可”。
重寫流程如下
- redis首先fork出一個子進程
- 子進程根據當前數據集把新的aof寫到一個臨時文件裏
- 主進程持續把新的變動寫到內存的buffer裏和舊的aof文件裏(這樣可以保證重寫失敗數據也不會丟失)
- 當子進程完成重寫後給主進程一個信號,然後把內存裏的buffer追加到新的aof裏。
- 替換新舊aof文件
在我看來,主進程將新的變動寫到舊的aof裏是一個備用手段,這樣可能降低了一部分性能,但保證了數據的安全
- 重寫是直接把當前內存的數據生成對應命令,並不需要讀取老的AOF文件進行分析、命令合併。
- 不管是RDB還是AOF都是先寫入一個臨時文件,然後通過
rename
完成文件的替換工作。
重寫相關的命令
# Redis會記住自從上一次重寫後AOF文件的大小(如果自Redis啓動後還沒重寫過,則記住啓動時使用的AOF文件的大小)。
# 如果當前的文件大小比起記住的那個大小超過指定的百分比,則會觸發重寫。
# 同時需要設置一個文件大小最小值,只有大於這個值文件纔會重寫,以防文件很小,但是已經達到百分比的情況。
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
# 要禁用自動的日誌重寫功能,我們可以把百分比設置爲0:
auto-aof-rewrite-percentage 0
# 手動觸發重寫
bgrewriteaof
數據損壞修復
如果因爲某些原因(例如服務器崩潰)AOF文件損壞了,導致Redis加載不了,可以通過以下步驟進行修復:
-
備份AOF文件。
-
使用
redis-check-aof
命令修復原始的AOF文件:
$ redis-check-aof --fix
-
可以使用
diff -u
命令看下兩個文件的差異。 -
使用修復過的文件重啓Redis服務。
從RDB切換到AOF
-
備份一個最新的
dump.rdb
的文件,並把備份文件放在一個安全的地方。 -
運行以下兩條命令:
$ redis-cli config set appendonly yes # 打開aof $ redis-cli config set save "" # 關閉rdb
-
確保數據跟切換前一致。
-
確保數據正確的寫到AOF文件裏。
-
如果有需要,更改redis.conf中啓用aof和關閉rdb,因爲命令行方式修改配置在重啓Redis後就會失效。
第二條命令是用來禁用RDB的持久化方式,但是這不是必須的,因爲你可以同時啓用兩種持久化方式。
備份
建議的備份方法:
- 創建一個定時任務,每小時和每天創建一個快照,保存在不同的文件夾裏。
- 定時任務運行時,把太舊的文件進行刪除。例如只保留48小時的按小時創建的快照和一到兩個月的按天創建的快照。
- 每天確保一次把快照文件傳輸到數據中心外的地方進行保存,至少不能保存在Redis服務所在的服務器。
線上性能建議
- 如果redis數據不是特別重要或者可以通過其他方式重寫生成數據,可以關閉持久化
- 自己制定策略定期檢查redis情況,然後可以手動觸發備份、重寫數據
- 單機多個實例的話,要防止多個機器同時運行持久化或重寫操作,防止出現內存、cpu、io資源競爭,讓持久化變爲串行
- 加入主從機器,從從機上進行備份
- rdb與aof可以同時存在,可以配合使用