[翻譯自官方]什麼是RDB和AOF? 一文了解Redis持久化!

曉兵之夢發表於2020-12-06

[翻譯自官方]什麼是RDB和AOF? 一文了解Redis持久化!

 

​概述


本文提供Redis持久化技術說明,  建議所有Redis使用者閱讀. 如果您想更深入瞭解Redis永續性原理機制和底層永續性保證, 請參考文章 揭祕Redis持久化: http://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(https://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(https://redis.io/commands/flushall) , 如果這時候重寫操作沒有被執行, 你仍然可以通過關閉服務, 刪除檔案最後的錯誤命令, 重啟Redis完成資料恢復.

 

AOF缺點


    • 對於相同的資料集, AOF檔案一般比RDB檔案大.

    • 根據具體的fsync策略, AOF可能比RDB速度慢. 通常預設的每秒fsync策略下, Reids效能也非常高, 如果禁用fsync, 即使在高負載的情況下, AOF的速度應該和RDB一樣快. 儘管如此, 在巨大寫負載的情況下, RDB提供了更多最大延遲的保證.

    • 在過去, 當執行一些特殊的命令時(比如這裡有一個涉及到阻塞的命令BRPOPLPUSH:https://redis.io/commands/brpoplpush), Redis遇到了一些罕見的BUG, 它會導致AOF重建資料時, 資料出現不一致.這些問題非常罕見, 我們進行了單元測試, 自動建立隨機複雜的資料集來執行重建測試, 沒有出現這些問題.  但是如果使用RDB持久化, 幾乎不可能出現這類問題. 為了清楚的說明這一點: AOF類似MySQL或者MongoDB, 採用增量更新現有狀態的工作機制, 但是RDB快照是每次從頭開始建立, 從概念上來說, RDB更具有魯棒性(健壯). 但是有以下兩點值得注意:

  1. 每次AOF被Redis重寫的時候,它會從包含在資料集中的實際資料中從頭開始重新建立,使新AOF檔案對bug的抵抗力比不重寫的, , 一直追加的AOF檔案更強.

  2. 在實際使用中, 我們重來沒有收到過一個關於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(https://redis.io/commands/bgrewriteaof) , 從Redis2.4開始, 它可以自動觸發日誌重寫(更多資訊可以檢視2.4的配置示例, 不同版本的配置(https://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官方文件: https://redis.io/topics/persistence


END已結束

[翻譯自官方]什麼是RDB和AOF? 一文了解Redis持久化!

歡迎大家留言, 訂閱, 交流哦!


[翻譯自官方]什麼是RDB和AOF? 一文了解Redis持久化!

往期回顧


Golang GinWeb框架9-編譯模板/自定義結構體繫結/http2/操作Cookie/完結

Golang GinWeb框架8-重定向/自定義中介軟體/認證/HTTPS支援/優雅重啟等

Golang GinWeb框架7-靜態檔案/模板渲染

Golang GinWeb框架6-XML/JSON/YAML/ProtoBuf等渲染

Golang GinWeb框架5-繫結請求字串/URI/請求頭/核取方塊/表單型別

Golang GinWeb框架4-請求引數繫結和驗證

Golang GinWeb框架3-自定義日誌格式和輸出方式/啟禁日誌顏色

Golang GinWeb框架2-檔案上傳/程式panic崩潰後自定義處理方式

Golang GinWeb框架-快速入門/引數解析

Golang與亞馬遜物件儲存服務AmazonS3快速入門

Golang+Vue實現Websocket全雙工通訊入門

GolangWeb程式設計之控制器方法HandlerFunc與中介軟體Middleware

Golang連線MySQL執行查詢並解析-告別結構體

Golang的一種釋出訂閱模式實現

Golang 併發資料衝突檢測器(Data Race Detector)與併發安全

Golang"驅動"MongoDB-快速入門("快碼加鞭")

 

相關文章