[翻譯自官方]什麼是RDB和AOF? 一文了解Redis持久化!
- 2020 年 12 月 6 日
- 筆記
概述
本文提供Redis持久化技術說明, 建議所有Redis用戶閱讀. 如果您想更深入了解Redis持久性原理機制和底層持久性保證, 請參考文章 揭秘Redis持久化: //antirez.com/post/redis-persistence-demystified.html
Redis持久化
Redis提供了不同級別的持久化選項:
-
RDB模式, Redis資料庫備份文件(Redis Database Backup)持久化方式, 提供周期性基於時間點的數據集快照備份, 比如每小時生成一個快照備份.
-
AOF模式, 僅追加到文件(AppendOnlyFile)持久化方式, 在每次資料庫服務收到寫操作時記錄日誌文件, 當服務重啟時, 自動回放該日誌來重建原始數據集. 日誌中使用Redis自己的協議, 並按照統一的格式, 採用只追加的方法記錄. 當日誌文件太大時, Redis可以在後台重寫該日誌, 生成一個最小化版本的日誌文件.
-
你也可以完全禁用持久化, 比如只要保證服務在運行中有數據或可以自動生成快取數據即可.
-
你還可以在同一個Redis實例上結合AOF和RDB兩種持久化方式. 請注意: 這種方式在Redis重啟時, AOF文件會被用來重建原始數據集, 因為, 相對RDB周期快照的方式, AOF被認為是更完整的數據備份, 比如它可以做到准實時備份(只丟失1秒的數據).
接下來, 讓我們來對比RDB和AOF的優缺點:
RDB優點
-
RDB採用一個壓縮單文件來表示基於時間點的Redis數據, RDB文件是完美的備份. 例如, 你可以保留過去24小時的每小時的快照備份, 並且保存過去30天, 每天的快照備份, 當數據遇到丟失時, 你可以很方便的從不同的備份粒度(版本)來恢複數據集.
-
RDB用來做災備恢復非常好, 因為緊湊的單文件非常便於在遠端數據中心或者亞馬遜S3(對象存儲,可以加密)間傳輸.
-
RDB使Redis性能最大化, 因為Redis父進程只需要啟動一個子進程完成快照備份即可, 父進程不執行由備份引起的磁碟I/O
-
與AOF模式相比, RDB在大數據集的情況下, 數據恢復時, 服務重啟速度更快.
RDB缺點
-
如果你想要在Redis意外停止工作時(比如斷電), 最小可能的丟失數據, RDB不是一個好的方案. 你可以在RDB生成的地方, 配置不同的保存點(比如每5分鐘,對數據集產生至少100次寫操作時,創建一個保存點, 你也可以配置多個保存點策略). 然而, 這樣你通常會在每5分鐘甚至更長時間間隔才創建RDB快照, 所以當Redis異常停止工作時, 你會丟失最後產生快照時間點到現在的數據.
-
RDB會調用系統fork()方法派生一個子進程來完成數據持久化到硬碟. 如果數據集比較大, Fork()方法會非常耗時, 造成Redis停止為客戶端服務, 停止時間可能是上微秒, 如果數據集非常大並且CPU性能不是很好, 停止時間可以達到1秒鐘或更多. 在持久化時, AOF也會調用fork()方法, 但是你可以不帶任何協商(trade-off), 調整重寫日誌的頻率.
AOF優點
使用AOF持久化程度更高: 你可以配置不同的fsync策略:
-
不帶fsync
-
每秒鐘一次fsync
-
每次查詢的時候fsync
注: fsync(//man7.org/linux/man-pages/man2/fsync.2.html)是系統方法, 用於將內核態的快取數據持久化到存儲設備, 比如將記憶體數據寫入硬碟
默認使用每秒執行一次fsync的策略, 這種場景下, Redis的寫性能也能非常好, 因為fsync運行在一個後台執行緒, 而主執行緒會儘力完成寫操作. 所以你最多丟失1秒鐘的數據.
-
AOF日誌是一個只能追加的文件, 所以在斷電後, 該文件不會出現查找(seek)或損壞的問題. 即使由於磁碟滿或其他原因導致日誌中存在只寫了一半的命令, 也可以使用
redis-check-aof
工具輕鬆修復. -
Redis會在AOF文件太大的時候, 自動在後台重寫日誌. 重寫十分安全, 重寫時, Redis派生一個子進程將大的AOF文件重寫為最小可用的數據集日誌文件, 此時有寫操作時, Redis繼續追加到舊的AOF文件的同時也追加到AOF重寫緩衝區aof_rewrite_buf, 重寫完成時, 新的小AOF文件將合併緩衝區中的新數據, 最後將新的AOF文件重命名為老的AOF文件完成替換操作, 以後的數據將寫入新的AOF文件.
-
AOF日誌文件以一種容易理解和解析的格式依次記錄了所有的操作. 導出一個AOF文件非常容易. 甚至在失誤執行了清除命令FLUSHALL(//redis.io/commands/flushall) , 如果這時候重寫操作沒有被執行, 你仍然可以通過關閉服務, 刪除文件最後的錯誤命令, 重啟Redis完成數據恢復.
AOF缺點
-
對於相同的數據集, AOF文件一般比RDB文件大.
-
根據具體的fsync策略, AOF可能比RDB速度慢. 通常默認的每秒fsync策略下, Reids性能也非常高, 如果禁用fsync, 即使在高負載的情況下, AOF的速度應該和RDB一樣快. 儘管如此, 在巨大寫負載的情況下, RDB提供了更多最大延遲的保證.
-
在過去, 當執行一些特殊的命令時(比如這裡有一個涉及到阻塞的命令BRPOPLPUSH://redis.io/commands/brpoplpush), Redis遇到了一些罕見的BUG, 它會導致AOF重建數據時, 數據出現不一致.這些問題非常罕見, 我們進行了單元測試, 自動創建隨機複雜的數據集來執行重建測試, 沒有出現這些問題. 但是如果使用RDB持久化, 幾乎不可能出現這類問題. 為了清楚的說明這一點: AOF類似MySQL或者MongoDB, 採用增量更新現有狀態的工作機制, 但是RDB快照是每次從頭開始創建, 從概念上來說, RDB更具有魯棒性(健壯). 但是有以下兩點值得注意:
-
每次AOF被Redis重寫的時候,它會從包含在數據集中的實際數據中從頭開始重新創建,使新AOF文件對bug的抵抗力比不重寫的, , 一直追加的AOF文件更強.
-
在實際使用中, 我們重來沒有收到過一個關於AOF文件出錯的用戶報告.
那我該使用哪個?
通常, 如果你想獲得像PostgreSQL那樣的數據安全性, 你應該結合RDB和AOF.
如果你非常關心你的數據, 但是允許丟失幾分鐘的數據, 你可以只使用RDB持久化.
有很多用戶只使用AOF, 但是我們不建議那樣做, 因為RDB的基於時間點的快照在做資料庫備份, 快速重啟, 或AOF引擎出現問題時, 非常有用.
注意: 基於這些原因, 在將來(長期計劃), 我們最終會統一AOF和RDB為一個持久化模型方案.
下面幾節, 我們來舉例說明更多, 關於RDB和AOF的細節.
快照
Redis默認保存快照到硬碟上的dump.rdb
文件. 你可以配置, 每N分鐘, 至少出現了M次數據集改變執行一次快照, 或者手動執行保存 SAVE 或後台保存BGSAVE 命令.
save 60 1000
它是如何工作的?
每當Redis需要保存數據集到磁碟, 會執行下面的任務:
-
Redis forks 派生子進程, 這時候會存在一個父進程和一個子進程.
-
子進程開始將數據集寫到RDB臨時文件.
-
當子進程完成新RDB文件寫入後, 會將原來的舊RDB文件替換.
這種方法就是Redis的寫即拷語義(copy-on-write)
AOF僅追加文件
快照不是很持久, 如果Redis服務異常停止, 掉電停止, 或者意外執行了kill -9
殺掉Redis服務進程, 最後的數據寫入將會丟失. 雖然對於有些應用來說這是個小問題, 但對於要求完全持久化的場景, RDB不是一個很好的選擇.
appendonly yes
從現在開始, 每當Redis收到一個改變數據集的命令(比如SET), 該操作將追加到AOF文件, 當你重啟Redis時, 會基於AOF文件重建數據集.
日誌重寫
AOF文件大小隨著操作的增加而增加. 舉個例子, 如果你想遞增計數100次, 最終數據集中只包含一個鍵值就是最終的結果, 但是在AOF文件中有100條記錄, 實際上在重建數據集時, 不需要剩餘的99次記錄.
所以Redis支援這個有趣的功能: 在不中斷Redis服務的情況下, 後台進行AOF文件重寫. 當執行後台重寫命令 BGREWRITEAOF 時, Reids會將當前記憶體中的數據集以最短的有序命令集寫下來. 如果你使用Redis2.2, 你需要定時執行 BGREWRITEAOF(//redis.io/commands/bgrewriteaof) , 從Redis2.4開始, 它可以自動觸發日誌重寫(更多資訊可以查看2.4的配置示例, 不同版本的配置(//redis.io/topics/config)).
AOF怎麼持久化?
你可以配置時間間隔, Redis來執行fsync
到磁碟. 這裡有三個策略:
-
appendfsync always
: 每個新的命令追加到AOF文件時執行fsync
. 非常慢, 但是非常安全. 注意, 如果追加的命令來自多個客戶端或管道的批量命令, 在發送響應之前, 這會被當做一次寫操作, 只會執行一次fsync. -
appendfsync everysec
: 每秒執行一次fsync
. 速度足夠快(在Redis2.4版本中, 與RDB快照的速度一樣快), 如果出現意外, 你最多丟失1秒的數據. -
appendfsync no
: 從不執行fsync
, 只把數據交給作業系統. 這雖然更快, 但是更不安全. 這種配置, 通常Linux會每30秒刷新一次數據到硬碟, 但實際時間可以通過內核配置調優.
每秒執行一次fsync
是建議並且是默認的方式. 它既快又安全. appendfsync always
策略在實踐中非常慢, 但是支援組提交, 所以可以將多個並行寫操作合併, 執行一次fsync
即可.
如果AOF文件被截斷了應該怎麼做?
在寫AOF文件時, 伺服器出現crash或磁碟空間滿了, 這時候AOF依然包含一致的數據, 代表了給定時間點版本的數據集(默認fsync策略可能會丟失1秒的數據), 但是最後的命令在AOF記錄中會被截斷, 最新的Redis主幹版本依然會導入所有的AOF文件內容, 但是會忽略最後的不完整的命令, 這時候, 伺服器會發出警告日誌:
* Reading RDB preamble from AOF file...
* Reading the remaining AOF tail...
# !!! Warning: short read while loading the AOF file !!!
# !!! Truncating the AOF at offset 439 !!!
# AOF loaded anyway because aof-load-truncated is enabled
你可以改變默認配置來強制停止這種事情發生, 但是默認配置會忽略最後這個不完整的命令, 為了保證服務重啟後可用.
老版本的Redis不會自動恢復, 需要做以下步驟來恢復:
-
對AOF文件進行備份.
-
使用Redis提供的工具
redis-check-aof
修復該AOF文件:$ redis-check-aof –fix
-
可以執行
diff -u
檢查兩個AOF文件的差異, 確認錯誤被修復. -
用修復後的AOF文件重啟Redis服務, 重建數據集.
AOF文件被損壞了怎麼辦?
如果AOF文件不僅被截斷了, 中間還被插入了無效的位元組, 事情將變得更加複雜, Redis在啟動的時候會中斷並提示:
* Reading the remaining AOF tail...
# Bad file format reading the append only file: make a backup of your AOF file, then use ./redis-check-aof --fix <filename>
最好是用 redis-check-aof
工具修復, 首先不適用 --fix
選項, 找到問題, 跳過該文件的錯誤位置, 查看是否可以手動修復該文件, AOF使用與Reids一致的協議格式,所以非常便於手動修復, 否則就使用工具修復該文件, 這種情況, 從無效的位置到文件結束的數據都可能被丟失, 如果損壞位置發生在開頭的位置, 則相當於丟失整個數據集.
它是怎樣工作的?
日誌重寫使用了與快照一致的拷貝即寫(copy-on-write)的方式, 步驟如下:
-
Redis執行 forks派生, 這樣就有一個主進程和一個子進程.
-
子進程開始寫入一個新的AOF到零時文件中.
-
Redis繼續追加到舊的AOF文件的同時也追加到AOF重寫緩衝區aof_rewrite_buf, 所以即使重新失敗, 也是數據安全的.
-
當子進程完成了AOF文件重寫, 父進程收到一個完成訊號, 將快取中的數據追加到新的AOF文件.
-
最後將新的AOF文件重命名為老的AOF文件完成替換操作, 以後的數據將寫入新的AOF文件.
怎樣從dump.rdb快照切換到AOF
在Redis2.0和Redis2.2用不同的步驟來切換到AOF, 而且Redis2.2切換到AOF更簡單, 不需要重啟.
Redis >= 2.2
-
將最近的dump.rdb文件備份.
-
將備份文件傳輸到安全的地方.
-
執行以下兩個命令:
-
redis-cli config set save “” #取消RDB
-
redis-cli config set appendonly yes #開啟AOF
-
檢查確認資料庫中的鍵個數沒有丟失.
-
檢查寫操作都正確的追加進了AOF文件.
第一個配置命令表示啟用AOF功能. 這樣Redis會阻塞來生成初始的備份, 然後打開新文件來寫入操作記錄, 後面的寫操作將會持續追加到該AOF文件中.
第二個配置命令用來關閉RDB快照持久化. 這是可選的, 如果保留save表示同時使用RDB和AOF持久化.
重要: 記住同時修改redis.conf配置文件來打開AOF, 否則服務重啟時將使用原來的配置.
Redis 2.0
-
將最近的dump.rdb文件備份.
-
將備份文件傳輸到安全的地方.
-
停止所有寫操作.
-
執行後台重寫AOF命令
redis-cli BGREWRITEAOF
. 該操作會創建AOF文件. -
當AOF備份完成後, 停止Redis服務.
-
編輯redis.conf, 啟用AOF功能.
-
重啟服務
-
檢查確認資料庫中的鍵個數沒有丟失.
-
檢查寫操作都正確的追加進了AOF文件.
在AOF和RDB之間交互
Redis >= 2.4會保證當RDB快照在運行時, 避免觸發一個AOF重寫進程, 或者當AOF重寫已經運行時, 不允許後台保存快照BGSAVE. 這可以防止兩個後台進程同時產生高負載的磁碟I/O.
備份Redis數據
開始本節內容前, 請確認已經對資料庫進行備份, 如果磁碟損壞, 雲實例消失等, 沒有備份意味著數據面臨著巨大風險, 會消失在”黑洞” /dev/null
中.
Redis對於數據備份非常友好, 即使資料庫資料庫運行中也允許你對數據進行拷貝備份: RDB文件產生時就不會被修改, 快照備份期間, 它會生成零時的文件, 當快照最終備份完成後採用重命名替換原來的RDB文件.
這意味著服務在運行時, 拷貝RDB文件是非常安全的, 下面是我們的建議:
-
在伺服器上, 創建定時任務CronJob, 每小時執行一次RDB快照, 保存到一個目錄, 並且在另外一個目錄下保存每日快照.
-
每次定時任務執行時, 確認使用
find
命令查找最舊的快照, 將它們刪除, 對於每小時快照, 你可以保留最近48小時, 對於每天快照, 你可以保留1~2個月. 並確包快照名包含時間資訊. -
每天至少做一次數據轉存, 比如將RDB快照轉存到其他數據中心, 或者至少從當前Redis服務物理機轉存到其他地方.
如果你使用ROF持久化方式, 仍然可以拷貝AOF文件來做備份. 這個AOF文件即使丟失最後一小段數據, Redis也可以重建它們(請參考上面的截斷AOF文件處理方式)
災難恢復
災難恢復和備份基本是一致的, 加上可以在許多不同的數據中心間轉存這些備份數據. 這種情況下, 即使影響到最主要的數據中心, 其他地方的備份也是安全並且可以恢復的.
針對剛起步, 沒有太多的資金來做大型備份, 這裡也提供了一些不需要太大開銷的災備恢復技術:
-
AmazonS3對象存儲或其他類似服務是一個實現災備恢復系統的好方法. 只需將每小時或每日的RDB快照加密後傳輸到S3即可, 你可以使用
gpg -c
(使用對稱加密模式)對數據加密. 請確認將密碼保存到不同的安全的地方(比如拷貝一份交給最重要的人來管理). 建議使用多種存儲服務來提高數據安全性. -
使用SCP(SSH的一部分)命令來將數據轉存到其他伺服器. 這是一個簡單而且安全的方法: 在雲端, 獲取遠離當前Redis服務的一個小型虛擬專用伺服器VPS, 在數據端, 安裝ssh, 生成不帶密碼的ssh客戶端密鑰, 將它添加到VPS的
authorized_keys
文件, 這樣就可以繼續實現自動免密轉存備份數據到VPS, 為了提高數據安全, 可以使用不同運營商, 不同網路區域的VPS.
這種方式可能會導致文件傳輸失敗, 所以在傳輸完成後, 至少要增加文件完整性校驗, 比如校驗文件大小, 如果使用VPS, 甚至可以使用SHA1校驗.
你也需要部署獨立的監控報警系統, 對備份過程進行監控, 在備份失敗時能及時發現並修復.
參考文檔
Redis官方文檔: //redis.io/topics/persistence
END已結束
歡迎大家留言, 訂閱, 交流哦!
往期回顧
Golang GinWeb框架9-編譯模板/自定義結構體綁定/http2/操作Cookie/完結
Golang GinWeb框架8-重定向/自定義中間件/認證/HTTPS支援/優雅重啟等
Golang GinWeb框架6-XML/JSON/YAML/ProtoBuf等渲染
Golang GinWeb框架5-綁定請求字元串/URI/請求頭/複選框/表單類型
Golang GinWeb框架3-自定義日誌格式和輸出方式/啟禁日誌顏色
Golang GinWeb框架2-文件上傳/程式panic崩潰後自定義處理方式
GolangWeb編程之控制器方法HandlerFunc與中間件Middleware
Golang 並發數據衝突檢測器(Data Race Detector)與並發安全
Golang”驅動”MongoDB-快速入門(“快碼加鞭”)