Redis的兩種持久化方式-快照持久化(RDB)和AOF持久化

OldBoy~發表於2017-05-17

Redis為了內部資料的安全考慮,會把本身的資料以檔案形式儲存到硬碟中一份,在伺服器重啟之後會自動把硬碟的資料恢復到記憶體(redis)的裡邊,資料儲存到硬碟的過程就稱為“持久化”效果。

redis有兩種持久化功能,一種是“快照持久化(RDB)”,一種是“AOF持久化”。

----------以下內容摘自《Redis深度歷險:核心原理和應用實踐》---------------

 Redis 的資料全部在記憶體裡,如果突然當機,資料就會全部丟失,因此必須有一種機制 來保證 Redis 的資料不會因為故障而丟失,這種機制就是 Redis 的持久化機制。

Redis 的持久化機制有兩種,第一種是快照,第二種是 AOF 日誌。快照是一次全量備 份,AOF 日誌是連續的增量備份。快照是記憶體資料的二進位制序列化形式,在儲存上非常緊湊,而 AOF 日誌記錄的是記憶體資料修改的指令記錄文字。AOF 日誌在長期的執行過程中會 變的無比龐大,資料庫重啟時需要載入 AOF 日誌進行指令重放,這個時間就會無比漫長。 所以需要定期進行 AOF 重寫,給 AOF 日誌進行瘦身。

快照原理

我們知道 Redis 是單執行緒程式,這個執行緒要同時負責多個客戶端套接字的併發讀寫操作 和記憶體資料結構的邏輯讀寫。

在服務線上請求的同時,Redis 還需要進行記憶體快照,記憶體快照要求 Redis 必須進行文 件 IO 操作,可檔案 IO 操作是不能使用多路複用 API。

這意味著單執行緒同時在服務線上的請求還要進行檔案 IO 操作,檔案 IO 操作會嚴重拖 垮伺服器請求的效能。還有個重要的問題是為了不阻塞線上的業務,就需要邊持久化邊響應 客戶端請求。持久化的同時,記憶體資料結構還在改變,比如一個大型的 hash 字典正在持久 化,結果一個請求過來把它給刪掉了,還沒持久化完呢,這尼瑪要怎麼搞?

那該怎麼辦呢?

Redis 使用作業系統的多程式 COW(Copy On Write) 機制來實現快照持久化,這個機制 很有意思,也很少人知道。多程式 COW 也是鑑定程式設計師知識廣度的一個重要指標。

fork(多程式)

Redis 在持久化時會呼叫 glibc 的函式 fork 產生一個子程式,快照持久化完全交給子進 程來處理,父程式繼續處理客戶端請求。子程式剛剛產生時,它和父程式共享記憶體裡面的代 碼段和資料段。這時你可以將父子程式想像成一個連體嬰兒,共享身體。這是 Linux 作業系統的機制,為了節約記憶體資源,所以儘可能讓它們共享起來。在程式分離的一瞬間,記憶體的增長几乎沒有明顯變化。子程式做資料持久化,它不會修改現有的記憶體資料結構,它只是對資料結構進行遍歷讀取,然後序列化寫到磁碟中。但是父程式不一樣,它必須持續服務客戶端請求,然後對記憶體資料結構進行不間斷的修改。這個時候就會使用作業系統的 COW 機制來進行資料段頁面的分離。資料段是由很多操 作系統的頁面組合而成,當父程式對其中一個頁面的資料進行修改時,會將被共享的頁面復 制一份分離出來,然後對這個複製的頁面進行修改。這時子程式相應的頁面是沒有變化的, 還是程式產生時那一瞬間的資料。

隨著父程式修改操作的持續進行,越來越多的共享頁面被分離出來,記憶體就會持續增 長。但是也不會超過原有資料記憶體的 2 倍大小。另外一個 Redis 例項裡冷資料佔的比例往 往是比較高的,所以很少會出現所有的頁面都會被分離,被分離的往往只有其中一部分頁 面。每個頁面的大小隻有 4K,一個 Redis 例項裡面一般都會有成千上萬的頁面。

子程式因為資料沒有變化,它能看到的記憶體裡的資料在程式產生的一瞬間就凝固了,再 也不會改變,這也是為什麼 Redis 的持久化叫「快照」的原因。接下來子程式就可以非常安 心的遍歷資料了進行序列化寫磁碟了。

AOF 原理

AOF 日誌儲存的是 Redis 伺服器的順序指令序列,AOF 日誌只記錄對記憶體進行修改的 指令記錄。

假設 AOF 日誌記錄了自 Redis 例項建立以來所有的修改性指令序列,那麼就可以通過 對一個空的 Redis 例項順序執行所有的指令,也就是「重放」,來恢復 Redis 當前例項的內 存資料結構的狀態。

Redis 會在收到客戶端修改指令後,先進行引數校驗,如果沒問題,就立即將該指令文 本儲存到 AOF 日誌中,也就是先存到磁碟,然後再執行指令。這樣即使遇到突發當機,已 經儲存到 AOF 日誌的指令進行重放一下就可以恢復到當機前的狀態。

Redis 在長期執行的過程中,AOF 的日誌會越變越長。如果例項當機重啟,重放整個AOF 日誌會非常耗時,導致長時間 Redis 無法對外提供服務。所以需要對 AOF 日誌瘦 身。

AOF 重寫

Redis 提供了 bgrewriteaof 指令用於對 AOF 日誌進行瘦身。其原理就是開闢一個子進 程對記憶體進行遍歷轉換成一系列 Redis 的操作指令,序列化到一個新的 AOF 日誌檔案中。 序列化完畢後再將操作期間發生的增量 AOF 日誌追加到這個新的 AOF 日誌檔案中,追加 完畢後就立即替代舊的 AOF 日誌檔案了,瘦身工作就完成了。

fsync

AOF 日誌是以檔案的形式存在的,當程式對 AOF 日誌檔案進行寫操作時,實際上是將 內容寫到了核心為檔案描述符分配的一個記憶體快取中,然後核心會非同步將髒資料刷回到磁碟 的。

這就意味著如果機器突然當機,AOF 日誌內容可能還沒有來得及完全刷到磁碟中,這個 時候就會出現日誌丟失。那該怎麼辦?

Linux 的 glibc 提供了 fsync(int fd)函式可以將指定檔案的內容強制從核心快取刷到磁 盤。只要 Redis 程式實時呼叫 fsync 函式就可以保證 aof 日誌不丟失。但是 fsync 是一個 磁碟 IO 操作,它很慢!如果 Redis 執行一條指令就要 fsync 一次,那麼 Redis 高效能的 地位就不保了。

所以在生產環境的伺服器中,Redis 通常是每隔 1s 左右執行一次 fsync 操作,週期 1s是可以配置的。這是在資料安全性和效能之間做了一個折中,在保持高效能的同時,儘可能 使得資料少丟失。

Redis 同樣也提供了另外兩種策略,一個是永不 fsync——讓作業系統來決定合適同步磁 盤,很不安全,另一個是來一個指令就 fsync 一次——非常慢。但是在生產環境基本不會使 用,瞭解一下即可。

運維

快照是通過開啟子程式的方式進行的,它是一個比較耗資源的操作。

1、遍歷整個記憶體,大塊寫磁碟會加重系統負載
2、AOF 的 fsync 是一個耗時的 IO 操作,它會降低 Redis 效能,同時也會增加系統 IO 負擔

所以通常 Redis 的主節點是不會進行持久化操作,持久化操作主要在從節點進行。從節 點是備份節點,沒有來自客戶端請求的壓力,它的作業系統資源往往比較充沛。 但是如果出現網路分割槽,從節點長期連不上主節點,就會出現資料不一致的問題,特別是在網路分割槽出現的情況下又不小心主節點當機了,那麼資料就會丟失,所以在生產環境要做好實時監控工作,保證網路暢通或者能快速修復。另外還應該再增加一個從節點以降低網路分割槽的概率,只要有一個從節點資料同步正常,資料也就不會輕易丟失。 

----------以上內容摘自《Redis深度歷險:核心原理和應用實踐》---------------

1.snap shotting快照持久化

該持久化預設開啟,一次性把redis中全部的資料儲存一份儲存在硬碟中,如果資料非常多(10-20G)就不合適頻繁操作該持久化操作。

如果對redis有資料操作,就會根據redis的配置檔案指定的檔名和路徑生成rdb檔案,先來看一下redis配置方面的截圖說明

再看一下在redis目錄下生成的rdb檔案

可以使用vim命令對dump.rdb檔案編輯看一下內容

注意:如果您細心的發現,在對redis客戶端進行資料操作之後,再次進行對dump.rdb檔案進行編輯檢視,檔案中可能會缺少最近的操作記錄,這與配置檔案中備份的頻率有關,下面看一下截圖

save 900 1             #900 秒內如果超過 1 個 key 被修改,則發起快照儲存
save 300 10            #300秒超過10個key被修改,發起快照
save 60 10000            #60秒超過10000個key被修改,發起快照

以上三個save的意思都有了相應的說明,資料修改的頻率非常高,備份的頻率也高,資料修改的頻率低,備份的頻率也低。

如果發現dump.rdb檔案缺少了最近的記錄,那麼在這補充一種手動持久化方式,可以立即看到效果,執行此命令

./redis-cli bgsave          #非同步儲存

 其次還包括一些其他的手動命令

./redis-cli shutdown    #同步儲存到伺服器並關閉redis伺服器
./redis-cli lastsave    #返回上次成功儲存到磁碟的unix時間戳
./redis-cli bgrewriteaof  #當日志檔案過長時優化AOF日誌檔案儲存

 由於快照方式是在一定間隔時間做一次的,所以如果redis意外down掉的話,就會丟失最後一次快照後的所有修改。如果應用要求不能丟失任何修改的話,可以採用aof持久化方式

2.append only file(AOF持久化)

AOF持久化本質:把使用者執行的每個“寫”指令(新增/修改/刪除)都備份到檔案中,還原資料的時候就是執行具體寫指令而已。

開啟AOF持久化(會清空redis內部的資料,最好在redis使用之前就開啟它)

我們在redis.conf配置檔案中可以找到它:

修改完成配置之後重啟redis

再次啟動測試一下是否有資料

看一下目錄下自定義的aof檔案,目前得大小是0

操作之後

vim檢視一下

所有指令全部寫入檔案

在redis.conf中,可以調整AOF備份形式:

always          一寫指令就備份一次。這樣做雖然安全,但是系統效能會降低。不推薦使用
everysec         每一秒中備份一次。不管一秒鐘變化了多少key,只備份一次,效能得到一定的保護。推薦使用。
no            會檢視當前伺服器狀態,如果狀態良好,就進行備份(隨機)。這種備份方式資料是沒有保證的。

對比下來,效能:always<everysec<no,而資料安全:always>everysec>no。

舉例說明一下,這裡就暫時不操作了,您可以拿redis的自增來操作:incr  num  ,執行此命令10次,然後檢視一下aof檔案,發現aof檔案儲存的是最終的數值,而且是set命令,這樣就節省了空間,下面說一下就是把AOF備份檔案中所有的備份資料的內容進行一個處理。

在啟動redis客戶端的時候,使用bgrewriteaof指令,就可以對aof備份檔案的內容進行

優化壓縮處理。截圖對比一下處理先後的大小

完畢

相關文章