1. 程式人生 > 資料庫 >4.1.2 Redis持久化, 原因, RDB方式(觸發,原理,結構,優缺點), AOF方式(原理,儲存模式,重寫,觸發方式, 混合持久化), RDB/AOF對比, 應用場景

4.1.2 Redis持久化, 原因, RDB方式(觸發,原理,結構,優缺點), AOF方式(原理,儲存模式,重寫,觸發方式, 混合持久化), RDB/AOF對比, 應用場景

目錄


 

Redis持久化

1. 為什麼要持久化

Redis是記憶體資料庫,宕機後資料會消失。訪問會打到DB上, 造成快取雪崩(大量key)
Redis重啟後快速恢復資料,要提供持久化機制
Redis持久化是為了快速的恢復資料而不是為了儲存資料

Redis有兩種持久化方式:RDB和AOF
注意:Redis持久化不保證資料的完整性。

當Redis用作DB時,DB資料要完整,所以一定要有一個完整的資料來源(檔案、mysql)
在系統啟動時,從這個完整的資料來源中將資料load到Redis中
資料量較小,不易改變,比如:字典庫(xml、Table)

通過info命令可以檢視關於持久化的資訊

# Persistence
loading:0
rdb_changes_since_last_save:1
rdb_bgsave_in_progress:0
rdb_last_save_time:1589363051
rdb_last_bgsave_status:ok
rdb_last_bgsave_time_sec:-1
rdb_current_bgsave_time_sec:-1
rdb_last_cow_size:0
aof_enabled:1
aof_rewrite_in_progress:0
aof_rewrite_scheduled:0
aof_last_rewrite_time_sec:-1

aof_current_rewrite_time_sec:-1
aof_last_bgrewrite_status:ok
aof_last_write_status:ok
aof_last_cow_size:0
aof_current_size:58
aof_base_size:0
aof_pending_rewrite:0
aof_buffer_length:0
aof_rewrite_buffer_length:0
aof_pending_bio_fsync:0
aof_delayed_fsync:0

 

2 RDB

RDB(Redis DataBase),是redis預設的儲存方式,RDB方式是通過快照( snapshotting )

完成的。

這一刻的資料
不關注過程

2.1 觸發快照的方式

1. 符合自定義配置的快照規則
2. 執行save或者bgsave命令
3. 執行flushall命令
4. 執行主從複製操作 (第一次)

配置引數定期執行

在redis.conf中配置:save<seconds><changes> 多少秒內 資料變了多少

save ""  # 不使用RDB儲存  不能主從
save 900 1  # 表示15分鐘(900秒鐘)內至少1個鍵被更改則進行快照。
save 300 10 # 表示5分鐘(300秒)內至少10個鍵被更改則進行快照。
save 60 10000 # 表示1分鐘內至少10000個鍵被更改則進行快照。

漏斗設計 提供效能

命令顯式觸發

在客戶端輸入bgsave命令。

127.0.0.1:6379> bgsave
Background saving started


2.2 RDB執行流程(原理)

1. Redis父程序首先判斷:當前是否在執行save,或bgsave/bgrewriteaof(aof檔案重寫命令)的子程序,如果在執行則bgsave命令直接返回。

2. 父程序執行fork(呼叫OS函式複製主程序)操作建立子程序,這個複製過程中父程序是阻塞的,Redis不能執行來自客戶端的任何命令。

3. 父程序fork後,bgsave命令返回”Background saving started”資訊並不再阻塞父程序,並可以響應其他命令。

4. 子程序建立RDB檔案,根據父程序記憶體快照生成臨時快照檔案,完成後對原有檔案進行原子替換。(RDB始終完整)

5. 子程序傳送訊號給父程序表示完成,父程序更新統計資訊。

6. 父程序fork子程序後,繼續工作。

 

2.3 RDB檔案結構

1、頭部5位元組固定為“REDIS”字串
2、4位元組“RDB”版本號(不是Redis版本號),當前為9,填充後為0009
3、輔助欄位,以key-value的形式


4、儲存資料庫號碼
5、字典大小
6、過期key
7、主要資料,以key-value的形式儲存
8、結束標誌
9、校驗和,就是看檔案是否損壞,或者是否被修改。
可以用winhex開啟dump.rdb檔案檢視。

 

2.4 RDB的優缺點

優點
RDB是二進位制壓縮檔案,佔用空間小,便於傳輸(傳給slaver)
主程序fork子程序,可以最大化Redis效能,主程序不能太大,Redis的資料量不能太大,複製過程中主程序阻塞

缺點
不保證資料完整性,會丟失最後一次快照以後更改的所有資料

 

3 AOF

AOF(append only file)是Redis的另一種持久化方式。Redis預設情況下是不開啟的。開啟AOF持久化後

Redis 將所有對資料庫進行過寫入的命令(及其引數(RESP)記錄到 AOF 檔案, 以此達到記錄資料庫狀態的目的,

這樣當Redis重啟後只要按順序回放這些命令就會恢復到原始狀態了。

AOF會記錄過程,RDB只管結果

 

3.1 AOF持久化實現

配置 redis.conf

# 可以通過修改redis.conf配置檔案中的appendonly引數開啟
appendonly yes 

# AOF檔案的儲存位置和RDB檔案的位置相同,都是通過dir引數設定的。
dir  ./ 

# 預設的檔名是appendonly.aof,可以通過appendfilename引數修改
appendfilename  appendonly.aof

 

3.2 AOF原理

AOF檔案中儲存的是redis的命令,同步命令到 AOF 檔案的整個過程可以分為三個階段:

命令傳播:Redis 將執行完的命令、命令的引數、命令的引數個數等資訊傳送到 AOF 程式中。

快取追加:AOF 程式根據接收到的命令資料,將命令轉換為網路通訊協議的格式,然後將協議內容追加到伺服器的 AOF 快取中。

檔案寫入和儲存:AOF 快取中的內容被寫入到 AOF 檔案末尾,如果設定的 AOF 儲存條件被滿足的話, fsync 函式或者 fdatasync 函式會被呼叫,將寫入的內容真正地儲存到磁碟中。

3.2.1 命令傳播

當一個 Redis 客戶端需要執行命令時, 它通過網路連線, 將協議文字傳送給 Redis 伺服器。伺服器在接到客戶端的請求之後, 它會根據協議文字的內容, 選擇適當的命令函式, 並將各個引數從字串文字轉換為 Redis 字串物件( StringObject )。每當命令函式成功執行之後, 命令引數都會被傳播到AOF 程式。

3.2.2 快取追加

當命令被傳播到 AOF 程式之後, 程式會根據命令以及命令的引數, 將命令從字串物件轉換回原來的協議文字。協議文字生成之後, 它會被追加到  redis.h/redisServer 結構的  aof_buf 末尾。

redisServer 結構維持著 Redis 伺服器的狀態,  aof_buf 域則儲存著所有等待寫入到 AOF 檔案的協議文字(RESP)。

3.2.3 檔案寫入和儲存

每當伺服器常規任務函式被執行、 或者事件處理器被執行時, aof.c/flushAppendOnlyFile 函式都會被呼叫, 這個函式執行以下兩個工作:
WRITE:根據條件,將 aof_buf 中的快取寫入到 AOF 檔案。
SAVE:根據條件,呼叫 fsync 或 fdatasync 的OS函式,將 AOF 檔案儲存到磁碟中。

 

3.3 AOF 儲存模式

Redis 目前支援三種 AOF 儲存模式,它們分別是:

AOF_FSYNC_NO :不儲存。

AOF_FSYNC_EVERYSEC :每一秒鐘儲存一次。(預設)

AOF_FSYNC_ALWAYS :每執行一個命令儲存一次。(不推薦)

以下三個小節將分別討論這三種儲存模式。

不儲存
在這種模式下, 每次呼叫 flushAppendOnlyFile 函式, WRITE 都會被執行, 但 SAVE 會被略過。
SAVE 只會在以下任意一種情況中被執行:

Redis 被關閉

AOF 功能被關閉

系統的寫快取被重新整理(可能是快取已經被寫滿,或者定期儲存操作被執行)

這三種情況下的 SAVE 操作都會引起 Redis 主程序阻塞。

每一秒鐘儲存一次(推薦)
在這種模式中, SAVE 原則上每隔一秒鐘就會執行一次, 因為 SAVE 操作是由後臺子執行緒(fork)呼叫的, 所以它不會引起伺服器主程序阻塞。

每執行一個命令儲存一次
在這種模式下,每次執行完一個命令之後, WRITE 和 SAVE 都會被執行。
另外,因為 SAVE 是由 Redis 主程序執行的,所以在 SAVE 執行期間,主程序會被阻塞,不能接受命令請求。

AOF 儲存模式對效能和安全性的影響
對於三種 AOF 儲存模式, 它們對伺服器主程序的阻塞情況如下:

 

3.4 AOF重寫、觸發方式、混合持久化

AOF記錄資料的變化過程,越來越大,需要重寫“瘦身”

Redis可以在 AOF體積變得過大時,自動地在後臺(Fork子程序)對 AOF進行重寫。重寫後的新 AOF檔案包含了恢復當前資料集所需的最小命令集合。 所謂的“重寫”其實是一個有歧義的詞語, 實際上,AOF 重寫並不需要對原有的 AOF 檔案進行任何寫入和讀取, 它針對的是資料庫中鍵的當前值。

舉例如下:
set s1 11
set s1 22 ------- > set s1 33
set s1 33

沒有優化的:
set s1 11
set s1 22
set s1 33

優化後:
set s1 33


lpush list1 1 2 3

lpush list1 4 5 6 -------- > list1 1 2 3 4 5 6

優化後
lpush list1 1 2 3 4 5 6

 

Redis 不希望 AOF 重寫造成伺服器無法處理請求, 所以 Redis 決定將 AOF 重寫程式放到(後臺)子程序裡執行, 這樣處理的最大好處是:

1、子程序進行 AOF 重寫期間,主程序可以繼續處理命令請求。

2、子程序帶有主程序的資料副本,使用子程序而不是執行緒,可以在避免鎖的情況下,保證資料的安全性。

不過, 使用子程序也有一個問題需要解決: 因為子程序在進行 AOF 重寫期間, 主程序還需要繼續處理命令, 而新的命令可能對現有的資料進行修改, 這會讓當前資料庫的資料和重寫後的 AOF 檔案中的資料不一致。

為了解決這個問題, Redis 增加了一個 AOF 重寫快取, 這個快取在 fork 出子程序之後開始啟用,Redis 主程序在接到新的寫命令之後, 除了會將這個寫命令的協議內容追加到現有的 AOF 檔案之外,還會追加到這個快取中。

3.4.1 重寫過程分析(整個重寫操作是絕對安全的):

Redis 在建立新 AOF 檔案的過程中,會繼續將命令追加到現有的 AOF 檔案裡面,即使重寫過程中發生停機,現有的 AOF 檔案也不會丟失。 而一旦新 AOF 檔案建立完畢,Redis 就會從舊 AOF 檔案切換到新 AOF 檔案,並開始對新 AOF 檔案進行追加操作。

當子程序在執行 AOF 重寫時, 主程序需要執行以下三個工作:

處理命令請求。

將寫命令追加到現有的 AOF 檔案中。

將寫命令追加到 AOF 重寫快取中。

這樣一來可以保證:

現有的 AOF 功能會繼續執行,即使在 AOF 重寫期間發生停機,也不會有任何資料丟失。 所有對資料庫進行修改的命令都會被記錄到 AOF 重寫快取中。 當子程序完成 AOF 重寫之後, 它會向父程序傳送一個完成訊號, 父程序在接到完成訊號之後, 會呼叫一個訊號處理函式, 並完成以下工作:

1. 將 AOF 重寫快取中的內容全部寫入到新 AOF 檔案中。

2. 對新的 AOF 檔案進行改名,覆蓋原有的 AOF 檔案。

Redis資料庫裡的+AOF重寫過程中的命令------->新的AOF檔案---->覆蓋老的

當步驟 1 執行完畢之後, 現有 AOF 檔案、新 AOF 檔案和資料庫三者的狀態就完全一致了。

當步驟 2 執行完畢之後, 程式就完成了新舊兩個 AOF 檔案的交替。

這個訊號處理函式執行完畢之後, 主程序就可以繼續像往常一樣接受命令請求了。 在整個 AOF 後臺重寫過程中, 只有最後的寫入快取和改名操作會造成主程序阻塞, 在其他時候, AOF 後臺重寫都不會對主程序造成阻塞, 這將 AOF 重寫對效能造成的影響降到了最低。

以上就是 AOF 後臺重寫, 也即是 BGREWRITEAOF 命令(AOF重寫)的工作原理。


3.4.2 觸發方式

1、配置觸發
在redis.conf中配置

# 表示當前aof檔案大小超過上一次aof檔案大小的百分之多少的時候會進行重寫。如果之前沒有重寫過,以啟動時aof檔案大小為準
auto-aof-rewrite-percentage 100

# 限制允許重寫最小aof檔案大小,也就是檔案大小小於64mb的時候,不需要進行優化
auto-aof-rewrite-min-size 64mb

2、執行bgrewriteaof命令

127.0.0.1:6379> bgrewriteaof
Background append only file rewriting started


3.4.3 混合持久化

RDB和AOF各有優缺點,Redis 4.0 開始支援 rdb 和 aof 的混合持久化。如果把混合持久化開啟,aof rewrite 的時候就直接把 rdb 的內容寫到 aof 檔案開頭。

RDB的頭+AOF的身體---->appendonly.aof

開啟混合持久化

aof-use-rdb-preamble yes 

我們可以看到該AOF檔案是rdb檔案的頭和aof格式的內容,在載入時,首先會識別AOF檔案是否以REDIS字串開頭,如果是就按RDB格式載入,載入完RDB後繼續按AOF格式載入剩餘部分。

 

3.4.4 AOF檔案的載入與資料還原

因為AOF檔案裡面包含了重建資料庫狀態所需的所有寫命令,所以伺服器只要讀入並重新執行一遍AOF檔案裡面儲存的寫命令,就可以還原伺服器關閉之前的資料庫狀態 Redis讀取AOF檔案並還原資料庫狀態的詳細步驟如下:

1、建立一個不帶網路連線的偽客戶端(fake client):因為Redis的命令只能在客戶端上下文中執行,而載入AOF檔案時所使用的命令直接來源於AOF檔案而不是網路連線,所以服 務器使用了一個沒有網路連線的偽客戶端來執行AOF檔案儲存的寫命令,偽客戶端執行命令 的效果和帶網路連線的客戶端執行命令的效果完全一樣

2、從AOF檔案中分析並讀取出一條寫命令

3、使用偽客戶端執行被讀出的寫命令

4、一直執行步驟2和步驟3,直到AOF檔案中的所有寫命令都被處理完畢為止

當完成以上步驟之後,AOF檔案所儲存的資料庫狀態就會被完整地還原出來,整個過程如下圖所示:

 

4 RDB與AOF對比

1、RDB存某個時刻的資料快照,採用二進位制壓縮儲存,AOF存操作命令,採用文字儲存(混合)
2、RDB效能高、AOF效能較低
3、RDB在配置觸發狀態會丟失最後一次快照以後更改的所有資料,AOF設定為每秒儲存一次,則最多丟2秒的資料
4、Redis以主伺服器模式執行,RDB不會儲存過期鍵值對資料,Redis以從伺服器模式執行,RDB會儲存過期鍵值對,當主伺服器向從伺服器同步時,再清空過期鍵值對。

AOF寫入檔案時,對過期的key會追加一條del命令,當執行AOF重寫時,會忽略過期key和del命令。

 

5 應用場景

記憶體資料庫 rdb+aof 資料不容易丟
有原始資料來源: 每次啟動時都從原始資料來源中初始化 ,則 不用開啟持久化 (資料量較小)
快取伺服器 rdb 一般 效能高

在資料還原時
有rdb+aof 則還原aof,因為RDB會造成檔案的丟失,AOF相對資料要完整。
只有rdb,則還原rdb

拉勾的配置策略

追求高效能:都不開 redis宕機 從資料來源恢復
字典庫 : 不驅逐,保證資料完整性 不開持久化

用作DB 不能主從 資料量小
做快取 較高效能: 開rdb
Redis資料量儲存過大,效能突然下降,
fork 時間過長 阻塞主程序
則只開啟AOF