Redis持久化神器!
天天用Redis,持久化方案有哪些你知道嗎?
前言?
Redis目前已經(jīng)成為主流的內(nèi)存數(shù)據(jù)庫了,但是大部分人僅僅是停留在會用的階段,你真的了解Redis內(nèi)部的工作原理嗎?
今天這篇文章將為大家介紹Redis持久化的兩種方案,文章將會從以下五個方面介紹:
什么是RDB,RDB如何實現(xiàn)持久化?
什么是AOF,AOF如何實現(xiàn)持久化?
AOF和RDB的區(qū)別。
如何重啟恢復(fù)數(shù)據(jù)?
持久化性能問題和解決方案
RDB
RDB持久化是把當(dāng)前進程數(shù)據(jù)生成快照保存到硬盤的過程, 觸發(fā)RDB持久化過程分為手動觸發(fā)和自動觸發(fā)。
RDB完成后會自動生成一個文件,保存在
dir配置的指定目錄下,文件名是dbfileName指定。Redis默認會采用LZF算法對生成的RDB文件做壓縮處理,壓縮后的文件遠遠小于內(nèi)存大小,默認開啟。
手動觸發(fā)
手動觸發(fā)的命令有
save和bgsave。save:該命令會阻塞Redis服務(wù)器,直到RDB的過程完成,已經(jīng)被廢棄,因此線上不建議使用。bgsave:每次進行RDB過程都會fork一個子進程,由子進程完成RDB的操作,因此阻塞只會發(fā)生在fork階段,一般時間很短。
自動觸發(fā)
除了手動觸發(fā)RDB,Redis服務(wù)器內(nèi)部還有如下幾個場景能夠自動觸發(fā)RDB:
根據(jù)我們的?
save m n?配置規(guī)則自動觸發(fā)。如果從節(jié)點執(zhí)行全量復(fù)制操作, 主節(jié)點自動執(zhí)行bgsave生成RDB文件并發(fā)送給從節(jié)點。
執(zhí)行
debug reload命令重新加載Redis時, 也會自動觸發(fā)save操作。默認情況下執(zhí)行shutdown命令時, 如果沒有開啟AOF持久化功能則自動執(zhí)行
bgsave。
RDB執(zhí)行流程
RDB的主流方式就是bgsave,通過下圖我們來看看RDB的執(zhí)行流程:
通過上圖可以很清楚RDB的執(zhí)行流程,如下:
執(zhí)行bgsave命令后,會先判斷是否存在AOF或者RDB的子進程,如果存在,直接返回。
父進程fork操作創(chuàng)建一個子進程,fork操作中父進程會被阻塞。
fork完成后,子進程開始根據(jù)父進程的內(nèi)存生成臨時快照文件,完成后對原有的RDB文件進行替換。執(zhí)行
lastsave命令可以查看最近一次的RDB時間。子進程完成后發(fā)送信號給父進程,父進程更新統(tǒng)計信息。
RDB的優(yōu)點
RDB是一個緊湊壓縮的二進制文件, 代表Redis在某個時間點上的數(shù)據(jù)快照。非常適用于備份, 全量復(fù)制等場景。比如每6小時執(zhí)行
bgsave備份,并把RDB文件拷貝到遠程機器或者文件系統(tǒng)中,用于災(zāi)難恢復(fù)。Redis加載
RDB恢復(fù)數(shù)據(jù)遠遠快于AOF的方式。
RDB的缺點
RDB方式數(shù)據(jù)沒辦法做到
實時持久化/秒級持久化。因為bgsave每次運行都要執(zhí)行fork操作創(chuàng)建子進程,屬于重量級操作,頻繁執(zhí)行成本過高。RDB文件使用特定二進制格式保存, Redis版本演進過程中有多個格式的RDB版本, 存在老版本Redis服務(wù)無法兼容新版RDB格式的問題。
AOF
AOF(append only file) 持久化:以獨立日志的方式記錄每次寫命令,重啟時再重新執(zhí)行AOF文件中的命令達到恢復(fù)數(shù)據(jù)的目的。AOF的主要作用是解決了數(shù)據(jù)持久化的實時性, 目前已經(jīng)是Redis持久化的主流方式。
如何開啟AOF
開啟AOF功能需要設(shè)置配置:
appendonly yes, 默認不開啟。AOF文件名通過appendfilename配置設(shè)置, 默認文件名是appendonly.aof。保存路徑同RDB持久化方式一致,通過dir配置指定。
AOF整體的執(zhí)行流程
AOF執(zhí)行的流程大致分為
命令寫入、文件同步、文件重寫、重啟加載四個步驟,如下圖:從上圖大致了解了AOF的執(zhí)行流程,下面一一分析上述的四個步驟。
命令寫入
AOF命令寫入的內(nèi)容直接是文本協(xié)議格式。例如
set hello world這條命 令, 在AOF緩沖區(qū)會追加如下文本:
*3\r\n$3\r\nset\r\n$5\r\nhello\r\n$5\r\nworld\r\n
命令寫入是直接寫入到AOF的緩沖區(qū)中,至于為什么?原因很簡單,Redis使用單線程響應(yīng)命令,如果每次寫AOF文件命令都直接追加到硬盤, 那么性能完全取決于當(dāng)前硬盤負載。先寫入緩沖區(qū)
aof_buf中, 還有另一個好處, Redis可以提供多種緩沖區(qū) 同步硬盤的策略,在性能和安全性方面做出平衡。
文件同步
Redis提供了多種AOF緩沖區(qū)同步文件策略, 由參數(shù)
appendfsync控制,如下:配置為
always時, 每次寫入都要同步AOF文件, 在一般的SATA硬盤上,Redis只能支持大約幾百TPS寫入, 顯然跟Redis高性能特性背道而馳,不建議配置。配置為
no,由于操作系統(tǒng)每次同步AOF文件的周期不可控,而且會加大每次同步硬盤的數(shù)據(jù)量,雖然提升了性能,但數(shù)據(jù)安全性無法保證。配置為
everysec(默認的配置),是建議的同步策略, 也是默認配置,做到兼顧性能和數(shù)據(jù)安全性。理論上只有在系統(tǒng)突然宕機的情況下丟失1秒的數(shù)據(jù)(當(dāng)然,這是不太準確的)。
文件重寫機制
隨著命令不斷寫入AOF, 文件會越來越大, 為了解決這個問題, Redis引入AOF重寫機制壓縮文件體積。AOF文件重寫是把Redis進程內(nèi)的數(shù)據(jù)轉(zhuǎn)化為寫命令同步到新AOF文件的過程。
為什么要文件重寫呢??因為文件重寫能夠使得AOF文件的體積變得更小,從而使得可以更快的被Redis加載。
重寫過程分為手動觸發(fā)和自動觸發(fā)。
手動觸發(fā)直接使用
bgrewriteaof命令。根據(jù)
auto-aof-rewrite-min-size和auto-aof-rewrite-percentage參數(shù)確定自動觸發(fā)時機。auto-aof-rewrite-min-size:表示運行AOF重寫時文件最小體積, 默認為64MB。auto-aof-rewrite-percentage:代表當(dāng)前AOF文件空間(aof_current_size) 和上一次重寫后AOF文件空間(aof_base_size) 的比值。自動觸發(fā)時機相當(dāng)于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統(tǒng)計信息中查看。那么文件重寫后的AOF文件為什么會變小呢??有如下幾個原因:
進程內(nèi)已經(jīng)超時的數(shù)據(jù)將不會再次寫入AOF文件中。
舊的AOF文件含有無效命令,如
del key1、?hdel key2等。重寫使用進程內(nèi)數(shù)據(jù)直接生成,這樣新的AOF文件只保留最終數(shù)據(jù)的寫入命令。多條寫命令可以合并為一個, 如:
lpush list a、?lpush list b、lpush listc可以轉(zhuǎn)化為:lpush list a b c。為了防止單條命令過大造成客戶端緩沖區(qū)溢出,對于list、?set、?hash、?zset等類型操作,以64個元素為界拆分為多條。介紹了文件重寫的系列知識,下面來看看Redis內(nèi)部是如何進行文件重寫的,如下圖:
看完上圖,大致了解了文件重寫的流程,對于重寫的流程,補充如下:
重寫期間,主線程并沒有阻塞,而是在執(zhí)行其他的操作命令,依然會向舊的AOF文件寫入數(shù)據(jù),這樣能夠保證備份的最終完整性,如果數(shù)據(jù)重寫失敗,也能保證數(shù)據(jù)不會丟失。
為了把重寫期間響應(yīng)的寫入信息也寫入到新的文件中,因此也會為子進程保留一個緩沖區(qū),防止新寫的文件丟失數(shù)據(jù)。
重寫是直接把當(dāng)前內(nèi)存的數(shù)據(jù)生成對應(yīng)命令,并不需要讀取老的AOF文件進行分析、命令合并。
AOF文件直接采用的
文本協(xié)議,主要是兼容性好、追加方便、可讀性高可認為修改修復(fù)。無論是
RDB還是AOF都是先寫入一個臨時文件,然后通過重命名完成文件的替換。
AOF的優(yōu)點
使用 AOF 持久化會讓 Redis 變得非常耐久:你可以設(shè)置不同的 fsync 策略,比如無 fsync ,每秒鐘一次 fsync ,或者每次執(zhí)行寫入命令時 fsync 。AOF 的默認策略為每秒鐘 fsync 一次,在這種配置下,Redis 仍然可以保持良好的性能,并且就算發(fā)生故障停機,也最多只會丟失一秒鐘的數(shù)據(jù)( fsync 會在后臺線程執(zhí)行,所以主線程可以繼續(xù)努力地處理命令請求)。
AOF的缺點
對于相同的數(shù)據(jù)集來說,AOF 文件的體積通常要大于 RDB 文件的體積。根據(jù)所使用的 fsync 策略,AOF 的速度可能會慢于 RDB。在一般情況下, 每秒 fsync 的性能依然非常高, 而關(guān)閉 fsync 可以讓 AOF 的速度和 RDB 一樣快, 即使在高負荷之下也是如此。不過在處理巨大的寫入載入時,RDB 可以提供更有保證的最大延遲時間。
數(shù)據(jù)恢復(fù)速度相對于RDB比較慢。
AOF和RDB的區(qū)別
RDB持久化是指在指定的時間間隔內(nèi)將內(nèi)存中的數(shù)據(jù)集快照寫入磁盤,實際操作過程是fork一個子進程,先將數(shù)據(jù)集寫入臨時文件,寫入成功后,再替換之前的文件,用二進制壓縮存儲。
AOF持久化以日志的形式記錄服務(wù)器所處理的每一個寫、刪除操作,查詢操作不會記錄,以文本的方式記錄,可以打開文件看到詳細的操作記錄。
重啟加載
無論是RDB還是AOF都可用于服務(wù)器重啟時的數(shù)據(jù)恢復(fù),執(zhí)行流程如下圖:
上圖很清晰的分析了Redis啟動恢復(fù)數(shù)據(jù)的流程,先檢查AOF文件是否開啟,文件是否存在,再檢查RDB是否開啟,文件是否存在。
性能問題與解決方案
通過上面的分析,我們都知道RDB的快照、AOF的重寫都需要fork,這是一個重量級操作,會對Redis造成阻塞。因此為了不影響Redis主進程響應(yīng),我們需要盡可能降低阻塞。
那么如何減少fork操作的阻塞呢?
優(yōu)先使用物理機或者高效支持fork操作的虛擬化技術(shù)。
控制Redis實例最大可用內(nèi)存, fork耗時跟內(nèi)存量成正比, 線上建議每個Redis實例內(nèi)存控制在10GB以內(nèi)。
合理配置Linux內(nèi)存分配策略,避免物理內(nèi)存不足導(dǎo)致fork失敗。
降低fork操作的頻率,如適度放寬AOF自動觸發(fā)時機,避免不必要的全量復(fù)制等。
