Redis持久化學習筆記(AOF與RDB詳細)

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裏的命令會被重新執行一次,重建數據。

工作過程

  1. 命令傳播:Redis 將執行完的命令、命令的參數、命令的參數個數等信息發送到 AOF 程序中。
  2. 緩存追加:AOF 程序根據接收到的命令數據,將命令轉換爲網絡通訊協議的格式,然後將協議內容追加到服務器的 AOF 緩存中。
  3. 文件寫入和保存: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加載不了,可以通過以下步驟進行修復:

  1. 備份AOF文件。

  2. 使用redis-check-aof命令修復原始的AOF文件:

$ redis-check-aof --fix
  1. 可以使用diff -u命令看下兩個文件的差異。

  2. 使用修復過的文件重啓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可以同時存在,可以配合使用

參考文章

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章