快速理解Redis的持久化
Redis系列文章
為什麼需要持久化
很簡單,因為 Redis
是基於記憶體的。資料如果不進行持久化,當伺服器重啟或者宕機的時候資料是無法恢復的,所以為了保證資料的安全性,我們需要將記憶體中的資料持久化到磁碟中。
Redis的持久化
Redis
提供了兩種持久化的方式,分別是 RDB
和 AOF
。
-
RDB :
Redis
的預設持久化方式,是基於 快照 來實現的,當符合一定條件的時候Redis
會自動將記憶體中的資料進行快照然後持久化到磁碟中。 -
AOF :
Redis
預設沒有開啟AOF
持久化,需要在配置檔案中設定appendonly true
Redis
的 順序指令序列 。
RDB方式的持久化
save 阻塞方式
在 Redis
中有一個命令可以觸發 RDB
持久化,但是這個操作會阻塞 Redis
。
這個命令是 save
,我們都知道 Redis
是單執行緒的,如果持久化進行 特殊的處理 的話,那麼就會阻塞其他命令導致 Redis
短時間內不可用,如果 RDB
檔案很大,那麼刷盤操作將會數十秒,嚴重影響可用性,所以我們一般都不會使用 save
命令。
bgsave 後臺方式
bgsave
顧名思義,就是 後臺進行儲存 。當執行這條命令的時候 Redis
就會進行一些 特殊處理 。
什麼特殊處理呢?
首先 Redis
glibc
的函式 fork
產生一個子程式,此時會將檔案 持久化全部交給子程式去處理,那麼這時父程式就可以繼續處理使用者的請求了(bgsave
執行之後直接會返回)。當然,在主程式進行 fork
操作的時候可能也會對使用者請求命令 產生短暫的阻塞 。
凡事有利必有弊,你看 bgsave
這麼好,難道沒有缺點麼? 答案是有的,在哪呢?我們先來瞭解一下 COW 機制吧。
COW
COW (copy on write),也就是 寫時複製 。我們知道 RDB
持久化需要遍歷記憶體中的資料,就像下面那張圖一樣。
因為我們需要的是在子程式產生的那一瞬間的資料(快照),如果此時因為使用者請求在主程式修改了記憶體中的資料,那麼子程式遍歷的記憶體就會被更改,這個時候就不是快照資料
所以這裡就使用了產生快照的一種機制—— COW 。我們知道上面的資料段其實由很多作業系統的頁組合而成的。COW 其實就是在主程式需要修改記憶體中的資料的時候,首先將需要修改的資料所在的頁進行復制,然後再複製的頁面上進行修改。當進行頁的複製的時候就會佔用額外的記憶體,這也是 bgsave
佔用記憶體比 save
多的原因,但是不用過分擔心,因為再儲存期間不會出現大量的使用者請求來修改資料,額外使用的記憶體也不會很多。
自動觸發RDB
在 Redis
中會有幾種情況下進行 RDB
的持久化,所以即使你在配置檔案中關閉了 RDB
,Redis
還是會進行 RDB
的持久化。
-
滿足條件時
在
Redis
的配置檔案中有這麼三條配置。save 900 1 save 300 10 save 60 10000 複製程式碼
這其實就是
RDB
中預設開啟的原因,它的格式是這樣的save seconds changeTimes
,save
後面的第一個數字是時間,第二個數字是修改次數,這三條配置的意思就是 在900秒內進行了1次修改或者在300秒內進行了10次修改或者在60秒內進行了10000次修改 會進行RDB
的自動持久化。 -
shutdown
當Redis
正常關閉的時候會進行RDB
持久化。 -
flushall
會產生一個空的RDB
檔案 -
主從複製進行全量複製的時候(目前做了解就行,我在後面的文章會講到 Redis 叢集)
RDB的優點
-
RDB
檔案,其中做了些壓縮,儲存的是資料,可以快速的進行災難恢復。 - 適合做冷備。
RDB的缺點
-
容易丟失資料,因為
RDB
需要遍歷整個記憶體中的所有資料,所以進行一次RDB
操作是一個費事費力的操作,為了保證Redis
的高效能,你需要儘量減少RDB
的持久化,所以你可能會丟失一段時間的資料。
AOF方式的持久化
預設情況下 Redis
是沒有開啟 AOF
持久化的,你需要在配置檔案中進行相應的配置。
appendonly yes # 預設為no這裡設定yes開啟
dir ./ # aof檔案目錄
appendfilename "appendonly-6379.aof" #aof檔名
複製程式碼
開啟 AOF
持久化之後,Redis
會根據 AOF
持久化策略 來進行相應的持久化操作,具體配置是在 appendfsync
配置。
# appendfsync always 每次進行修改操作就進行寫盤
appendfsync everysec 每秒進行一些寫盤
# appendfsync no 從不
複製程式碼
Redis
一共提供了三個引數,一般考慮設定 everysec
每秒寫盤,這樣既能少影響效率也能減少資料丟失量。
AOF原理
AOF
日誌中存放的是對於 Redis
的操作指令,有了 AOF
日誌我們就可以使用它進行對 Redis
的重放。
比如此時我們 AOF
日誌中記錄了 set hello world
和 sadd userset FancisQ
這兩條命令,我們就可以對一個空的 Redis
例項進行此 AOF
檔案的重放,最終這個空的 Redis
就有了上面兩條記錄。
你可能會發現 Redis
中的這兩個持久化方式很像 MySQL
中的 bin log
和 redo log
,但是你需要注意的是 Redis
中的 AOF
是先執行命令再存日誌的。這和 MySQL
中的 WAL 機制截然相反。
為什麼呢? 我覺得有兩點。
-
Redis
是弱事務的,我們不需要保證資料的強一致。在MySQL
中我們使用了redo log
兩階段提交 來保證了save-crash
能力,而在Redis
中我們顯然不需要這麼做,假設這條命令執行完之後還沒來得及寫日誌就宕機了,那就沒了,因為弱事務,我們大可不必保證資料必須存在。 - 為了避免錯誤指令的日誌儲存,如果先寫日誌也就意味著我們一開始沒有做相應的 邏輯處理和引數校驗 ,所以這樣會 先記錄到很多錯誤指令 ,但是我們知道
AOF
檔案是需要 瘦身 的,這些錯誤指令會給AOF
瘦身帶來很多麻煩。
AOF重寫
上面提到的 瘦身 其實就是 AOF重寫 ,我們知道 AOF
檔案中儲存的是指令順序,當 Redis
長時間執行時會產生很多指令。
比如
set a b
,set a c
,set a d
.....
其實上面三條就是對 key
為 a 的資料進行操作了,在 RDB
中它可能只存了 a = d
,但是因為 AOF
的指令機制,它必須存在三條,但前面的是無意義的,這樣會浪費很多空間並且給 AOF
重放帶來麻煩。
所以 Redis
會在 AOF
檔案過大(符合某種條件)的時候進行自動的 AOF
重寫。對應的在配置檔案中有這樣兩條配置。
# 下面兩條需要同時滿足
# 表示當前 aof 檔案超過上次 aof檔案大小的百分之多少時會進行重寫,如果沒有重寫過則以啟動時的大小為標準
auto-aof-rewrite-percentage 100
# 檔案大於多少的時候進行重寫
auto-aof-rewrite-min-size 64mb
複製程式碼
那麼,AOF
是如何重寫的呢?
bgrewriteaof
這是一條 AOF
重寫命令(上述的重寫過程其實就是 bgrewriteaof
),和 bgsave
一樣,Redis
也是會 fork
一個子程式,讓子程式去負責 AOF
檔案的重寫。大致流程如下:
AOF的優缺點
- 缺點: 在同等資料量的情況下,
AOF
檔案的大小要比RDB
檔案大得多,如果使用它進行記憶體狀態恢復需要花費很長時間。 - 優點: 持久化快,能減少資料丟失的量,在配置
everysec
的情況下最多隻會丟失秒級的資料。
Redis混合持久化
在 Redis
4.0之前,我們一般會開啟 AOF
然後再需要恢復記憶體狀態的時候棄用 AOF日誌重放 ( 如果使用RDB的話會丟失大量資料 )。但如果 Redis
例項很大,AOF
檔案也很大的時候會導致 Redis
重啟非常慢。
為瞭解決這個問題,在 Redis
4.0之後我們可以將 RDB
檔案和 AOF
增量日誌儲存在一起。如果這個時候我們進行記憶體狀態恢復可以先使用前面的 RDB
部分,然後再使用 RDB
持久化之後產生的增量AOF日誌 來進行記憶體狀態恢復以減少時間。
如何選擇 RDB 和 AOF
- 一般來說如果對資料的安全性還是有一定要求的話,應該同時使用兩種持久化功能。
- 如果可以承受分鐘級別的資料丟失可以僅僅使用
RDB
。 -
AOF
儘量使用everysec
配置,既能保證資料安全又能保證效能效率。 -
RDB
下關閉save seconds changeTimes
這個自動持久化機制,或者合理使用引數。
思維圖譜
感謝閱讀。 給你們分享一下思維導圖 (#^.^#)