關於Redis,也許你想要了解這些內容(二)--持久化

上一次咱們介紹了Redis的數據類型,應用場景,單線程模型以及與Memcached的比較.今天繼續介紹它的持久化.mysql

持久化

Redis的數據是存在內存裏的,若是不採起特殊措施,一旦redis重啓/退出/故障,裏面的數據將所有丟失.linux

即使它很快重啓也只是個空的redis,這時大量的請求將會直接打到DB去.web

爲了不這種狀況,Redis提供了RDB和AOF兩種持久化方式.redis

RDB


RDB也就是快照,持久化時是把當前內存中的數據集快照寫入磁盤(名爲dump.rdb的二進制文件),恢復時則把快照文件直接讀到內存中.sql

RDB文件的建立數據庫

兩條命令:安全

  • SAVE阻塞Redis服務器進程,服務器不能接收任何請求,直到RDB文件建立完畢爲止。
  • BGSAVE建立出一個 子進程,由子進程來負責建立RDB文件,服務器進程能夠繼續接收請求。bgsave期間服務器會拒絕任何save命令(防止競態).

這兩條命令既能夠由客戶端來請求執行,也能夠在知足下列條件時自動觸發:服務器

  • 用戶 設置了save配置選項,好比save 60 10000,那麼從最近一次建立快照以後開始算起,當"60秒內有10000次寫入"這個條件知足時,redis就會 自動觸發bgsave命令.若是設置了多條,知足其中一條就會觸發.若是用戶沒有主動設置,redis也有本身的默認配置
  • 收到 shutdown(關閉服務器)或者標準term信號(linux下終止進程)後,redis會執行save命令,阻塞全部客戶端,再也不執行客戶端發送的任何命令,並在save命令執行完畢後關閉服務器.
  • 主從複製時,主服務器向從服務器發送一條sync命令,若是此時主服務器沒在執行bgsave,也不是剛剛執行完bgsave,就會執行bgsave命令.

save命令的操做比較簡單,可是它在執行期間會阻塞線上的業務,因此通常咱們想手動備份的時候都是用的bgsave.要了解bgsave,必須先清楚什麼是COW,什麼是fork.app

cow也就是copy on write,寫時複製.這是一種讀寫分離的設計思想.編輯器

假設有一塊內存空間,咱們要對它進行讀寫,那麼當只有讀的需求時,不須要額外操做.若是有寫的需求時,另外開一塊新的內存空間,把須要寫的那頁複製到這裏,而後就在新的空間進行修改,而寫的同時仍舊能夠讀舊的空間.寫完以後把指針指向新的空間,舊的空間拋棄.

這樣設計的好處是讀寫分離,不須要加鎖來形成阻塞,寫的同時也能夠讀,提升了讀的效率.(聯想到CopyOnWriteList沒有?)

而fork是linux系統的一個進程機制,應用到了cow.當父進程fork出一個子進程時,二者共享內存裏面的代碼段和數據段.

RDB的具體執行過程以下:

  • redis調用 forks建立子進程來進行rdb操做.
  • 子進程將數據集寫入到一個臨時rdb文件中,父進程繼續提供讀寫服務.此時父進程和子進程共享數據段,能夠用到 copy-on-write機制.
  • 沒必要擔憂父進程的讀寫會改變子進程持久化的數據,由於此時子進程已經把內存的數據 固定下來了.父進程讀的時候,依然如舊. 父進程寫的時候,子進程會把髒頁(即將被寫入的那頁)複製一份出來讓父進程去寫.子進程寫入硬盤的仍是舊的那頁.
  • 當子進程完成對rdb文件的寫入時,redis用新的rdb文件替換原來的rdb文件.而fork時產生的那些髒頁也會替換原來未修改的那些頁.舊頁被拋棄.

須要注意的是,在bgsave這個過程當中,服務器執行的寫命令並無一塊兒同步到rdb文件中,也就是說,RDB文件在bgsave以後並不與當前的數據庫狀態一致.

RDB文件的載入

rdb文件是在服務器啓動的時候自動載入的,redis沒有提供任何主動載入rdb文件的命令.

只要redis在啓動的時候檢測到rdb文件的存在,就會主動載入它.

可是AOF文件的載入優先級高於RDB.只有AOF功能關閉時,redis纔會用rdb文件來恢復數據.

tcvxsg.png
tcvxsg.png

AOF


aof(append only file)與rdb文件存儲數據集不一樣,aof文件存儲的是redis全部執行過的命令,相似於mysql的binlog.

AOF的實現

分爲三步:

  • 命令追加
  • 文件寫入
  • 文件同步

這裏要先介紹現代操做系統的文件寫入機制:

爲了提升效率,在現代操做系統中,當用戶試圖寫入文件時,這些數據會先寫入到操做系統的內存緩衝區裏,這就是上面說的文件寫入.

等到內存緩衝區被填滿了,或者超過必定時限後,操做系統才真正把內存緩衝區裏的數據寫入到硬盤的文件中去.這就是文件同步.

服務器在執行完一個寫命令後,就會把這條命令追加到redis的aof緩衝區的末尾.

服務器每次在結束一個事件循環以前,都會根據配置項appendfsync來判斷是否要把緩衝區的命令寫入到aof文件.

appendfsync有三種選項:

  • always : 將aof緩衝區的數據 寫入並同步到aof文件中.
  • everysec(默認) : 寫入文件,若是距離上次同步aof文件的時間超過了1秒,那就再同步.
  • no : 只寫入文件,不一樣步.同步時間由操做系統決定.

使用always,當redis故障時數據丟失最少,但寫入硬盤的操做頻繁,但效率最慢.

使用no,不只故障時丟失數據最多,並且當緩衝區等待寫入硬盤的數據填滿時,redis的寫操做將被阻塞,因此平均效率與always至關.

使用everysec不只安全,並且實際效率與不進行持久化時相差無幾.推薦使用everysec.

AOF的載入

因爲aof裏包含了redis執行過的全部寫命令,當服務器重啓時,只需讀取並執行aof文件裏的全部命令便可還原數據庫狀態.

可是因爲redis的命令只能在客戶端上下文中執行,所以這個還原的過程其實是藉助於僞客戶端來進行的.

tcvzLQ.png
tcvzLQ.png

AOF重寫(bgrewriteaof)

RDB文件和AOF文件隨着服務器的運行,數據愈來愈多,文件的體積也會膨脹起來.RDB文件沒有辦法優化,可是AOF能夠經過AOF重寫來減小體積.

AOF存儲的是命令集,有時候對於一條數據反覆設置能夠產生多條命令(過程).因爲還原數據庫只須要最終的數據(結果),所以能夠想辦法把這多條命令合併到一條命令上.好比aof記錄了set a 1, set a 2,set a 3三條命令,那麼咱們最終只須要set a 3這條等效命令.

實際上,AOF重寫並不依賴AOF文件,而是從數據庫一個個讀取key如今的值,而後把一條條set命令寫入到新aof文件去.寫完以後用新aof文件替代舊aof文件,那麼重寫就完成了.

AOF後臺重寫

aof重寫須要讀取所有的key,若是隻經過一個線程去操做的話,無疑會形成嚴重阻塞,在此期間沒法繼續處理客戶端的請求(有沒有想到keys命令?)

因此Redis將重寫放入到子進程去進行,讓父進程繼續處理客戶端的讀寫請求.(和bgsave類似).

一樣地,在重寫期間,若是父進程繼續修改數據庫的數據,那麼重寫後的aof文件與當前數據庫的狀態並不一致.

爲了解決這種數據不一致的問題,redis設置了一個aof重寫緩衝區(與aof緩衝區區分開),在fork子進程以後使用.因此在重寫期間,服務器進程會執行如下三個工做:

  • 執行客戶端發來的命令.
  • 將執行後的寫命令追加到aof緩衝區末尾.
  • 將執行後的寫命令追加到aof重寫緩衝區末尾.(新增)
tcvvQS.png
tcvvQS.png

當子進程重寫完成後,會把aof重寫緩衝區裏的命令(未優化過)寫入到新的aof文件裏,這樣就能夠保證新aof文件與數據庫狀態的一致性了.

因此要注意的是,即使aof剛剛重寫完畢,它也是還能夠再壓縮的.

順便一提,主從複製時也會用到這個機制.

RDB與AOF對比

  • RDB存儲數據集,每次持久化是 全量存儲.AOF存儲命令集,每次持久化只存儲 增量.
  • RDB持久化時間更長,可能丟失的數據也更多.
  • RDB文件通常會小於AOF文件(未重寫),用於還原數據庫狀態時的速度也會快於AOF
  • 可是因爲aof文件的更新頻率更高,丟失數據的數量也更小,所以服務器啓動時默認載入aof文件.
  • 因爲RDB文件緊湊性,便於複製數據到一個遠端數據中心,很是適用於災難恢復,適合 冷備.
  • AOF更新頻率快,可能丟失的數據少,適合 熱備.

爲了防止服務器忽然宕機的狀況,Redis在運行期間必定要開啓aof進行備份.

可是若是Redis是正常退出,它會自動生成RDB文件,這個RDB文件保存的是最新的數據集.那麼重啓時,能夠把aof文件刪掉,載入這個RDB文件以快速恢復.

RDB和AOF混合搭配模式

在對redis進行恢復的時候,若是咱們採用了RDB的方式,由於bgsave的策略,可能會致使咱們丟失大量的數據。若是咱們採用了AOF的模式,經過AOF操做日誌重放恢復,重放AOF日誌比RDB要長久不少。

redis4.0以後,爲了解決這個問題,引入了新的持久化模式,混合持久化,將rdb的文件和局部增量的AOF文件相結合,rdb可使用相隔較長的時間保存策略,aof不須要是全量日誌,只須要保存前一次rdb存儲開始到這段時間增量aof日誌便可,通常來講,這個日誌量是很是小的。

相關文章
相關標籤/搜索