大家好,我是小羽。
Redis 的讀寫都是在記憶體中進行的,所以它的效能高。而當我們的伺服器斷開或者重啟的時候,資料就會消失,那麼我們該怎麼解決這個問題呢?
其實 Redis 已經為我們提供了一種持久化的機制,分別是 RDB
和 AOF
兩種方式,接下來跟著我一起看看這兩個錦囊都是怎麼保證資料的持久化的。
持久化
由於 Redis 是基於記憶體的資料庫,所以當伺服器出現故障的時候,我們的資料就得不到安全保障。
這個時候就需要將記憶體中的資料儲存到磁碟中,當我們伺服器重啟時,便可以通過磁碟來恢復資料,這個過程就叫做 Redis 持久化。
Redis持久化
RDB
簡介
RDB全稱Redis Database Backup file
(Redis資料備份檔案),也可以稱為Redis資料快照。
RDB 檔案是一個經過壓縮的二進位制檔案(預設:dump.rdb);
RDB 檔案儲存在硬碟裡;
通過儲存資料庫中的鍵值對來記錄資料庫狀態。
建立
當 Redis 持久化時,程式會將當前記憶體中的資料庫狀態儲存到磁碟中。
建立
建立 RDB 檔案主要有兩個 Redis 命令:SAVE
和 BGSAVE
。
SAVE
同步操作,執行命令時,會阻塞 Redis 伺服器程序,拒絕客戶端傳送的命令請求。
程式碼示例:
def SAVE():
# 建立 RDB 檔案
rdbSave()
圖示:
Save命令
BGSAVE
非同步操作,執行命令時,子程序執行儲存工作,伺服器還可以繼續讓主執行緒處理客戶端傳送的命令請求。
程式碼示例:
def BGSAVE():
# 建立子程序
pid = fork()
if pid == 0:
# 子程序負責建立 RDB 檔案
rdbSave()
# 完成之後向父程序傳送訊號
signal_parent()
elif pid > 0:
# 父程序繼續處理命令請求,並通過輪訓等待子程序的訊號
handle_request_and_wait_signal()
else:
handle_fork_error()
圖示:
bgSave命令
載入
載入工作在伺服器啟動時自動執行。
載入
伺服器在載入 RDB 檔案期間,會一直處於阻塞狀態,直到載入工作完成為止。
主要設定
Redis 允許使用者通過設定伺服器配置的 save 選項,讓伺服器每隔一段時間自動執行一次 BGSAVE 命令。
設定儲存條件
提供配置如下:
save 900 1
save 300 10
在這種情況下,只要滿足以下條件中的一個,BGSAVE 命令就會被執行:
伺服器在 900 秒之內,對資料庫進行了至少 1 次修改了;
伺服器在 300 秒之內,對資料庫進行了至少 10 次修改。
saveparams
伺服器程式會根據 save 選項所設定的儲存條件,設定伺服器狀態 redisServer 結構的 saveparams
屬性。
saveparams
屬性是一個數組;陣列中的每一個元素都是一個
saveparam
結構;每個
saveparam
結構都儲存了一個save
選項設定的儲存條件。
struct saveparam {
// 秒數
time_t seconds;
// 修改數
int changes;
}
dirty
dirty
計數器記錄距離上一次成功執行 SAVE 命令或 BGSAVE 命令之後,伺服器對資料庫狀態進行了多少次修改(包括寫入、刪除、更新等操作)。
lastsave
是一個 UNINX
時間戳,記錄了伺服器上一次成功執行 SAVE 命令或者 BGSAVE 命令的時間。
檢查儲存條件是否滿足
伺服器週期性操作函式 serverCron
(該函式對正在執行的伺服器進行維護)預設每隔 100 毫秒就會執行一次,其中一項工作就是檢查 save 選項所設定的儲存條件是否已經滿足,滿足的話就執行 BGSAVE 命令。
程式碼示例:
def serverCron():
# ....
# 遍歷所有儲存條件
for saveparam in server.saveparams:
# 計算距離上次執行儲存操作有多少秒
save_interval = unixtime_now() - server.lastsave
# 如果資料庫狀態的修改次數超過條件所設定的次數
# 如果距離上次儲存的時間超過條件所設定的時間
if server.dirty >= saveparam.changes and save_interval > saveparam.seconds:
BGSAVE()
預設配置
RDB 檔案預設的配置如下:
################################ SNAPSHOTTING ################################
#
# Save the DB on disk:
#在給定的秒數和給定的對資料庫的寫運算元下,自動持久化操作。
# save <seconds> <changes>
#
save 900 1
save 300 10
save 60 10000
#bgsave發生錯誤時是否停止寫入,一般為yes
stop-writes-on-bgsave-error yes
#持久化時是否使用LZF壓縮字串物件?
rdbcompression yes
#是否對rdb檔案進行校驗和檢驗,通常為yes
rdbchecksum yes
# RDB持久化檔名
dbfilename dump.rdb
#持久化檔案儲存目錄
dir ./
AOF
簡介
AOF全稱為 Append Only File
(追加日誌檔案)。日誌是寫後日志,Redis 是先執行命令,把資料寫入記憶體,然後才記錄日誌。
寫後日志
通過儲存 Redis 伺服器所執行的寫命令來記錄資料庫狀態;
寫入 AOF 檔案的所有命令都是以 Redis 的命令請求協議格式儲存的。
實現
AOF 持久化流程實現主要是通過以下流程來實現的:
AOF流程
命令追加
若 AOF 持久化功能處於開啟狀態,伺服器在執行完一個命令後,會以協議格式將被執行的寫命令追加到伺服器狀態的 aof_buf
緩衝區的末尾。
檔案同步
伺服器每次結束一個事件迴圈之前,都會呼叫 flushAppendOnlyFile
函式,這個函式會考慮是否需要將 aof_buf
緩衝區中的內容寫入和儲存到 AOF 檔案裡。
flushAppendOnlyFile
函式執行以下流程:
WRITE:根據條件,將 aof_buf 中的快取寫入到 AOF 檔案;
SAVE:根據條件,呼叫 fsync 或 fdatasync 函式,將 AOF 檔案儲存到磁碟中。
這個函式是由伺服器配置的 appendfsync
的三個值:always、everysec、no
來影響的,也被稱為三種策略。
Always
每條命令都會 fsync 到硬碟中,這樣 redis 的寫入資料就不會丟失。
Always
everysec
每秒都會重新整理緩衝區到硬碟中(預設值)。
everysec
no
根據當前作業系統的規則決定什麼時候重新整理到硬碟中,不需要我們來考慮。
no
資料載入
建立一個不帶網路連線的偽客戶端;
從 AOF 檔案中分析並讀取出一條寫命令;
使用偽客戶端執行被讀出的寫命令;
一直執行步驟 2 和 3,直到 AOF 檔案中的所有寫命令都被處理完畢為止。
檔案重寫
為何需要檔案重寫:
為了解決 AOF 檔案體積膨脹的問題;
通過重寫建立一個新的 AOF 檔案來替代現有的 AOF 檔案,新的 AOF 檔案不會包含任何浪費空間的冗餘命令。
實現
檔案重寫的實現原理:
不需要對現有的 AOF 檔案進行任何操作;
從資料庫中直接讀取鍵現在的值;
用一條命令記錄鍵值對,從而代替之前記錄這個鍵值對的多條命令。
後臺重寫
為不阻塞父程序,Redis 將 AOF 重寫程式放到子程序裡執行。
在子程序執行 AOF 重寫期間,伺服器程序需要執行三個流程:
執行客戶端發來的命令;
將執行後的寫命令追加到 AOF 緩衝區;
將執行後的寫命令追加到 AOF 重寫緩衝區。
伺服器流程
預設配置
AOF 檔案預設的配置如下:
############################## APPEND ONLY MODE ###############################
#開啟AOF持久化方式
appendonly no
#AOF持久化檔名
appendfilename "appendonly.aof"
#每秒把緩衝區的資料fsync到磁碟
appendfsync everysec
# appendfsync no
#是否在執行重寫時不同步資料到AOF檔案
no-appendfsync-on-rewrite no
# 觸發AOF檔案執行重寫的增長率
auto-aof-rewrite-percentage 100
#觸發AOF檔案執行重寫的最小size
auto-aof-rewrite-min-size 64mb
#redis在恢復時,會忽略最後一條可能存在問題的指令
aof-load-truncated yes
#是否開啟混合開關
aof-use-rdb-preamble yes
總結
通過以上的簡介,想必大家都對 Redis 持久化有了大致的瞭解,那麼這兩種方式,我們該如何選擇呢?
對於大中型的應用,我們既想保證資料完整性又想保證高效率,就應該結合使用 RDB 和 AOF 兩種方式;
如果只是需要保證資料的完整性,保護資料不會丟失,那麼優先使用 AOF 方式;
如果是處理大規模的資料恢復,追求更高更快的效率的話,優先使用 RDB 方式。
也可以參照下圖進行選擇:
主要對比