最新:Redis持久化——AOF日誌redis
咱們都知道Redis是內存數據庫,它將本身的數據存儲的內存中。這樣一旦服務器進程退出(斷電、重啓等緣由),那麼數據將會丟失。爲了解決這個問題,Redis提供兩種持久化的方式來將數據持久化到硬盤上,即內存快照(RDB)與AOF日誌。
所謂內存快照,顧名思義就是給內存拍個照,在某個時刻把內存中的數據記錄下來,以文件的形式保存到硬盤上,這樣即便宕機,數據依然存在。在服務器重啓後只須要把「照片」中的數據恢復便可。
RDB持久化就是把當前進程的數據在某個時刻生成快照(一個壓縮的二進制文件)保存到硬盤的過程,觸發RDB持久化過程分爲手動觸發和自動觸發。
RDB文件的建立能夠手動觸發,也能夠自動觸發。
手動觸發分別對應save和bgsave命令:
save命令會阻塞當前Redis服務器,直到RDB過程完成爲止。在服務器進程阻塞期間,服務器不能處理任何命令請求。所以,當save命令正在執行時,客戶端發送的全部命令都會被拒絕,知道save命令執行完畢。
redis>save //等待,直到RDB文件建立完畢 ok
注意:
Redis的單線程模型就決定了,咱們要儘可能避免全部會阻塞主線程的操做,因爲Save命令執行期間阻塞服務器進程,對於內存比較大的實例會形成長時間阻塞,所以線上環境不建議使用。
bgsave命令會派生出一個子進程(而不是線程),由子進程進行RDB文件建立,而父進程繼續處理命令。
redis>bgsave Background saving started //直接返回,由子進程進行RDB文件建立 redis> //繼續處理其它命令
注意:
由於bgsave命令能夠在不阻塞服務器進程的狀況下保存,因此redis能夠經過設置服務器配置的save選項,讓服務器每隔一段時間自動執行一次bgsave命令。如:咱們向服務器設置以下配置(這也是redis默認的配置):
save 900 1 save 300 10 save 60 10000
那麼只要知足以下條件中的一個bgsave命令就會被執行:
在Redis啓動的時候,只要檢測到RDB文件的存在,就會自動加載RDB文件。須要注意的是
由於AOF文件的更新頻率一般比RDB文件的更新頻率高,因此口若是服務器開啓了AOF持久化功能,那麼服務器會優先使用AOF文件來還原數據庫狀態。
只有在AOF持久化功能處於關閉狀態時,服務器纔會使用RDB文件來還原數據庫狀態。
注意:服務器在載入RDB文件期間,會一直處於阻塞狀態,直到載入工做完成爲止
瞭解了什麼是Redis的RDB持久化,咱們來思考兩個問題。
Redis RDB持久化是對某一時刻
的內存中的全量數據
進行拍照。這讓咱們不得不思考,快照的時候數據能夠修改嗎?
首先,若是咱們使用save
命令作持久化,那麼因爲Redis單線程模型的緣由,在持久化的過程當中會阻塞,是不能執行其它命令的。也許有人會說可使用bgave
命令,但使用bgsave
就沒有問題了嗎?
咱們在拍照的時候,一般攝影師是不讓咱們動的,由於一動可能照片就模糊了。在Redis 進行內存快照的時候也會如此。若是咱們持久化的過程當中,有些數據被修改了。那麼就會破壞快照的正確性與完整性。
好比在t時刻,咱們對內存進行快照,此時咱們但願的是記錄下來t時刻內存中全部的數據,假設咱們的RDB操做須要10s的時間,而t+2s咱們執行了一個修改操做把Key1
的值由A修改爲了B,而此時RDB操做卻尚未把Key1
的值寫入磁盤。在t+5s的時候讀取到key1
的值寫入磁盤。那麼這次快照記錄的Key1
的值就是B,而不是t時刻的A。這樣就破壞了RDB文件的正確性。
RDB文件的生成是須要時間的,若是快照執行期間數據不能被修改,對於業務系統來講不能接受的。那麼Redis 是如何解決這個問題的呢?
Redis 藉助了操做系統提供的寫時複製技術(Copy-On-Write, COW),可讓在執行快照的同時,正常處理寫操做。簡單來講,bgsave fork子進程的時候,並不會徹底複製主進程的內存數據,而是隻複製必要的虛擬數據結構,並不爲其分配真實的物理空間,它與父進程共享同一個物理內存空間。bgsave 子進程運行後,開始讀取主線程的內存數據,並把它們寫入 RDB 文件。此時,若是主線程對這些數據也都是讀操做,那麼,主線程和 bgsave 子進程相互不影響。可是,若是主線程要修改一塊數據,此時會給子進程分配一塊物理內存空間,把要修改的數據複製一份,生成該數據的副本到子進程的物理內存空間。而後,bgsave 子進程會把這個副本數據寫入 RDB 文件,而在這個過程當中,主線程仍然能夠直接修改原來的數據。
假設咱們在t 時刻作了一次快照,而後又在 t+n 時刻作了一次快照,而在這期間,發生了數據修改。而此時宕機了,那麼,只能按照 t 時刻的快照進行恢復。那麼這n秒的數據就完全丟失沒法恢復了。
因此,要想盡量恢復數據,就只能縮短快照執行的時間間隔,間隔的時間越小,丟失數據也就越少。那麼能夠頻繁的執行快照操做嗎?
咱們知道bgsave 執行時並不阻塞主線程,可是這不表明能夠頻繁執行快照操做。
一方面,持久化是一個寫入磁盤的過程,頻繁將全量數據寫入磁盤,會給磁盤帶來很大壓力,頻繁執行快照也容易致使前一個快照尚未執行完,後一個又開始了,這樣多個快照競爭有限的磁盤帶寬,容易形成惡性循環。
再者,bgsave所fork出來的子進程執行操做雖然並不會阻塞父進程的操做,可是fork
出子進程的操做倒是由主進程完成的,會阻塞主進程,fork子進程須要拷貝進程必要的數據結構,其中有一項就是拷貝內存頁表(虛擬內存和物理內存的映射索引表),這個拷貝過程會消耗大量CPU資源,拷貝完成以前整個進程是會阻塞的,阻塞時間取決於整個實例的內存大小,實例越大,內存頁表越大,fork阻塞時間也就越久。
也許有人會想到是否能夠作增量快照呢?也就是隻對上一次快照以後的數據作快照。
首先思路確定是能夠,可是增量快照要求記住哪些數據上一次快照以後產生的。這就須要額外的元數據來記錄這些信息,會引入額外的空間消耗。這對於內存資源寶貴的 Redis 來講,並非一個很好的方案。
若是不能頻繁執行快照操做,那麼該如何解決兩次快照之間的數據丟失的問題呢?Redis 還提供了另一種持久化方式——AOF(append to file)日誌。
關於AOF日誌請看Redis持久化——AOF日誌