前言
Redis目前已經成爲主流的內存數據庫了,但是大部分人僅僅是停留在會用的階段,你真的瞭解Redis內部的工作原理嗎?
今天這篇文章將爲大家介紹Redis持久化的兩種方案,文章將會從以下五個方面介紹:
什麼是RDB,RDB如何實現持久化?
什麼是AOF,AOF如何實現持久化?
AOF和RDB的區別。
如何重啓恢復數據?
持久化性能問題和解決方案
RDB
RDB持久化是把當前進程數據生成快照保存到硬盤的過程, 觸發RDB持久化過程分爲手動觸發和自動觸發。
RDB完成後會自動生成一個文件,保存在
dir
配置的指定目錄下,文件名是dbfileName
指定。Redis默認會採用LZF算法對生成的RDB文件做壓縮處理,壓縮後的文件遠遠小於內存大小,默認開啓。
手動觸發
手動觸發的命令有
save
和bgsave
。save
:該命令會阻塞Redis服務器,直到RDB的過程完成,已經被廢棄,因此線上不建議使用。bgsave
:每次進行RDB過程都會fork一個子進程,由子進程完成RDB的操作,因此阻塞只會發生在fork階段,一般時間很短。
自動觸發
除了手動觸發RDB,Redis服務器內部還有如下幾個場景能夠自動觸發RDB:
根據我們的
save m n
配置規則自動觸發。如果從節點執行全量複製操作, 主節點自動執行bgsave生成RDB文件併發送給從節點。
執行
debug reload
命令重新加載Redis時, 也會自動觸發save操作。默認情況下執行shutdown命令時, 如果沒有開啓AOF持久化功能則自動執行
bgsave
。
RDB執行流程
RDB的主流方式就是bgsave,通過下圖我們來看看RDB的執行流程:
通過上圖可以很清楚RDB的執行流程,如下:
執行bgsave命令後,會先判斷是否存在AOF或者RDB的子進程,如果存在,直接返回。
父進程fork操作創建一個子進程,fork操作中父進程會被阻塞。
fork完成後,子進程開始根據父進程的內存生成臨時快照文件,完成後對原有的RDB文件進行替換。執行
lastsave
命令可以查看最近一次的RDB時間。子進程完成後發送信號給父進程,父進程更新統計信息。
RDB的優點
RDB是一個緊湊壓縮的二進制文件, 代表Redis在某個時間點上的數據快照。非常適用於備份, 全量複製等場景。比如每6小時執行
bgsave
備份,並把RDB文件拷貝到遠程機器或者文件系統中,用於災難恢復。Redis加載
RDB
恢復數據遠遠快於AOF
的方式。
RDB的缺點
RDB方式數據沒辦法做到
實時持久化
/秒級持久化
。因爲bgsave每次運行都要執行fork操作創建子進程,屬於重量級操作,頻繁執行成本過高。RDB文件使用特定二進制格式保存, Redis版本演進過程中有多個格式的RDB版本, 存在老版本Redis服務無法兼容新版RDB格式的問題。
AOF
AOF
(append only file) 持久化:以獨立日誌的方式記錄每次寫命令,重啓時再重新執行AOF文件中的命令達到恢復數據的目的。AOF的主要作用是解決了數據持久化的實時性, 目前已經是Redis持久化的主流方式
。
如何開啓AOF
開啓AOF功能需要設置配置:
appendonly yes
, 默認不開啓。AOF文件名通過appendfilename
配置設置, 默認文件名是appendonly.aof
。保存路徑同RDB持久化方式一致,通過dir
配置指定。
AOF整體的執行流程
AOF執行的流程大致分爲
命令寫入
、文件同步
、文件重寫
、重啓加載
四個步驟,如下圖:從上圖大致瞭解了AOF的執行流程,下面一一分析上述的四個步驟。
命令寫入
AOF命令寫入的內容直接是文本協議格式。例如
set hello world
這條命 令, 在AOF緩衝區會追加如下文本:
*3\r\n$3\r\nset\r\n$5\r\nhello\r\n$5\r\nworld\r\n
命令寫入是直接寫入到AOF的緩衝區中,至於爲什麼?原因很簡單,Redis使用單線程響應命令,如果每次寫AOF文件命令都直接追加到硬盤, 那麼性能完全取決於當前硬盤負載。先寫入緩衝區
aof_buf
中, 還有另一個好處, Redis可以提供多種緩衝區 同步硬盤的策略,在性能和安全性方面做出平衡。
文件同步
Redis提供了多種AOF緩衝區同步文件策略, 由參數
appendfsync
控制,如下:配置爲
always
時, 每次寫入都要同步AOF文件, 在一般的SATA硬盤上,Redis只能支持大約幾百TPS寫入, 顯然跟Redis高性能特性背道而馳,不建議配置。配置爲
no
,由於操作系統每次同步AOF文件的週期不可控,而且會加大每次同步硬盤的數據量,雖然提升了性能,但數據安全性無法保證。配置爲
everysec
(默認的配置),是建議的同步策略, 也是默認配置,做到兼顧性能和數據安全性。理論上只有在系統突然宕機的情況下丟失1秒的數據(當然,這是不太準確的)。
文件重寫機制
隨着命令不斷寫入AOF, 文件會越來越大, 爲了解決這個問題, Redis引入AOF重寫機制壓縮文件體積。AOF文件重寫是把Redis進程內的數據轉化爲寫命令同步到新AOF文件的過程。
爲什麼要文件重寫呢? 因爲文件重寫能夠使得AOF文件的體積變得更小,從而使得可以更快的被Redis加載。
重寫過程分爲手動觸發和自動觸發。
手動觸發直接使用
bgrewriteaof
命令。根據
auto-aof-rewrite-min-size
和auto-aof-rewrite-percentage
參數確定自動觸發時機。auto-aof-rewrite-min-size
:表示運行AOF重寫時文件最小體積, 默認爲64MB。auto-aof-rewrite-percentage
:代表當前AOF文件空間(aof_current_size
) 和上一次重寫後AOF文件空間(aof_base_size
) 的比值。自動觸發時機相當於aof_current_size>auto-aof-rewrite-minsize&&(aof_current_size-aof_base_size) /aof_base_size>=auto-aof-rewritepercentage。其中
aof_current_size
和aof_base_size
可以在info Persistence
統計信息中查看。那麼文件重寫後的AOF文件爲什麼會變小呢? 有如下幾個原因:
進程內已經超時的數據將不會再次寫入AOF文件中。
舊的AOF文件含有無效命令,如
del key1
、hdel key2
等。重寫使用進程內數據直接生成,這樣新的AOF文件只保留最終數據的寫入命令。多條寫命令可以合併爲一個, 如:
lpush list a
、lpush list b
、lpush listc
可以轉化爲:lpush list a b c
。爲了防止單條命令過大造成客戶端緩衝區溢出,對於list
、set
、hash
、zset
等類型操作,以64個元素爲界拆分爲多條。
介紹了文件重寫的系列知識,下面來看看Redis內部是如何進行文件重寫的,如下圖:
看完上圖,大致瞭解了文件重寫的流程,對於重寫的流程,補充如下:
重寫期間,主線程並沒有阻塞,而是在執行其他的操作命令,依然會向舊的AOF文件寫入數據,這樣能夠保證備份的最終完整性,如果數據重寫失敗,也能保證數據不會丟失。
爲了把重寫期間響應的寫入信息也寫入到新的文件中,因此也會爲子進程保留一個緩衝區,防止新寫的文件丟失數據。
重寫是直接把當前內存的數據生成對應命令,並不需要讀取老的AOF文件進行分析、命令合併。
AOF文件直接採用的
文本協議
,主要是兼容性好、追加方便、可讀性高可認爲修改修復。無論是
RDB
還是AOF
都是先寫入一個臨時文件,然後通過重命名
完成文件的替換。
AOF的優點
使用 AOF 持久化會讓 Redis 變得非常耐久:你可以設置不同的 fsync 策略,比如無 fsync ,每秒鐘一次 fsync ,或者每次執行寫入命令時 fsync 。AOF 的默認策略爲每秒鐘 fsync 一次,在這種配置下,Redis 仍然可以保持良好的性能,並且就算髮生故障停機,也最多隻會丟失一秒鐘的數據( fsync 會在後臺線程執行,所以主線程可以繼續努力地處理命令請求)。
AOF的缺點
對於相同的數據集來說,AOF 文件的體積通常要大於 RDB 文件的體積。根據所使用的 fsync 策略,AOF 的速度可能會慢於 RDB。在一般情況下, 每秒 fsync 的性能依然非常高, 而關閉 fsync 可以讓 AOF 的速度和 RDB 一樣快, 即使在高負荷之下也是如此。不過在處理巨大的寫入載入時,RDB 可以提供更有保證的最大延遲時間。
數據恢復速度相對於RDB比較慢。
AOF和RDB的區別
RDB持久化是指在指定的時間間隔內將內存中的數據集快照寫入磁盤,實際操作過程是fork一個子進程,先將數據集寫入臨時文件,寫入成功後,再替換之前的文件,用二進制壓縮存儲。
AOF持久化以日誌的形式記錄服務器所處理的每一個寫、刪除操作,查詢操作不會記錄,以文本的方式記錄,可以打開文件看到詳細的操作記錄。
重啓加載
無論是RDB還是AOF都可用於服務器重啓時的數據恢復,執行流程如下圖:
上圖很清晰的分析了Redis啓動恢復數據的流程,先檢查AOF文件是否開啓,文件是否存在,再檢查RDB是否開啓,文件是否存在。
性能問題與解決方案
通過上面的分析,我們都知道RDB的快照、AOF的重寫都需要fork,這是一個重量級操作,會對Redis造成阻塞。因此爲了不影響Redis主進程響應,我們需要儘可能降低阻塞。
那麼如何減少fork操作的阻塞呢?
優先使用物理機或者高效支持fork操作的虛擬化技術。
控制Redis實例最大可用內存, fork耗時跟內存量成正比, 線上建議每個Redis實例內存控制在10GB以內。
合理配置Linux內存分配策略,避免物理內存不足導致fork失敗。
降低fork操作的頻率,如適度放寬AOF自動觸發時機,避免不必要的全量複製等。
總結
本文介紹了Redis持久化的兩種不同的策略,大部分內容是運維人員需要掌握的,當然作爲後端人員也是需要了解一下,畢竟小公司都是一人搞全棧,哈哈。
如果覺得陳某寫的不錯,有所收穫的話,關注分享一波,你的關注將是陳某寫作的最大動力,謝謝支持!!!
文章留言區
往期推薦
天天寫 order by,你知道Mysql底層執行流程嗎?萬字長文帶你入門Zookeeper!!!Mysql性能優化:如何給字符串加索引?SpringBoot與Maven多環境整合!!!靚仔,整合SpringBoot還在搜配置嗎?老司機教你一招!!!老司機帶你聊聊接口限流!!!Spring解決循環依賴,你真的懂了嗎?