面試寶典系列-讀《深刻學習Redis(2):持久化》概要

原文連接:深刻學習Redis(2):持久化html

Redis的5種對象類型(字符串、哈希、列表、集合、有序集合)redis

查看內存方法:info memory數據庫

內存碎片是Redis在分配、回收物理內存過程當中產生的。例如,若是對數據的更改頻繁,並且數據之間的大小相差很大,可能致使redis釋放的空間在物理內存中並無釋放,但redis又沒法有效利用,這就造成了內存碎片。內存碎片不會統計在used_memory中。安全

內存碎片的產生與對數據進行的操做、數據的特色等都有關;此外,與使用的內存分配器也有關係:若是內存分配器設計合理,能夠儘量的減小內存碎片的產生。後面將要說到的jemalloc便在控制內存碎片方面作的很好。服務器

解決內存碎片:若是Redis服務器中的內存碎片已經很大,能夠經過安全重啓的方式減少內存碎片:由於重啓以後,Redis從新從備份文件中讀取數據,在內存中進行重排,爲每一個數據從新選擇合適的內存dan元,減少內存碎片。網絡

jemalloc做爲Redis的默認內存分配器,在減少內存碎片方面作的相對比較好。jemalloc在64位系統中,將內存空間劃分爲小、大、巨大三個範圍;每一個範圍內又劃分了許多小的內存塊單位;當Redis存儲數據時,會選擇大小最合適的內存塊進行存儲。app

jemalloc劃分的內存dan元以下圖所示:負載均衡

 

在Redis中,實現高可用的技術主要包括持久化、複製、哨兵和集羣,下面分別說明它們的做用,以及解決了什麼樣的問題。函數

  1. 持久化:持久化是最簡單的高可用方法(有時甚至不被歸爲高可用的手段),主要做用是數據備份,即將數據存儲在硬盤,保證數據不會因進程退出而丟失。
  2. 複製:複製是高可用Redis的基礎,哨兵和集羣都是在複製基礎上實現高可用的。複製主要實現了數據的多機備份,以及對於讀操做的負載均衡和簡單的故障恢復。缺陷:故障恢復沒法自動化;寫操做沒法負載均衡;存儲能力受到單機的限制。
  3. 哨兵:在複製的基礎上,哨兵實現了自動化的故障恢復。缺陷:寫操做沒法負載均衡;存儲能力受到單機的限制。
  4. 集羣:經過集羣,Redis解決了寫操做沒法負載均衡,以及存儲能力受到單機限制的問題,實現了較爲完善的高可用方案。

持久化的功能:Redis是內存數據庫,數據都是存儲在內存中,爲了不進程退出致使數據的永久丟失,須要按期將Redis中的數據以某種形式(數據或命令)從內存保存到硬盤;當下次Redis重啓時,利用持久化文件實現數據恢復。除此以外,爲了進行災難備份,能夠將持久化文件拷貝到一個遠程位置。性能

Redis持久化分爲RDB持久化AOF持久化:前者將當前數據保存到硬盤,後者則是將每次執行的寫命令保存到硬盤(相似於MySQL的binlog);因爲AOF持久化的實時性更好,即當進程意外退出時丟失的數據更少,所以AOF是目前主流的持久化方式,不過RDB持久化仍然有其用武之地。 

RDB持久化:

將當前進程中的數據生成快照保存到硬盤(所以也稱做快照持久化),保存的文件後綴是rdb;當Redis從新啓動時,能夠讀取快照文件恢復數據。

手動觸發:save命令和bgsave命令均可以生成RDB文件。

save命令會阻塞Redis服務器進程,直到RDB文件建立完畢爲止,在Redis服務器阻塞期間,服務器不能處理任何命令請求(save基本被廢棄)。而bgsave命令會建立一個子進程,由子進程來負責建立RDB文件,父進程(即Redis主進程)則繼續處理請求。

自動觸發:自動觸發最多見的狀況是在配置文件中經過save m n,指定當m秒內發生n次變化時,會觸發bgsave。

Redis的save m n,是經過serverCron函數、dirty計數器、和lastsave時間戳來實現的。

serverCron是Redis服務器的週期性操做函數,默認每隔100ms執行一次;該函數對服務器的狀態進行維護,其中一項工做就是檢查 save m n 配置的條件是否知足,若是知足就執行bgsave。

dirty計數器是Redis服務器維持的一個狀態,記錄了上一次執行bgsave/save命令後,服務器狀態進行了多少次修改(包括增刪改);而當save/bgsave執行完成後,會將dirty從新置爲0。

lastsave時間戳也是Redis服務器維持的一個狀態,記錄的是上一次成功執行save/bgsave的時間。

RDB載入:

RDB文件的載入工做是在服務器啓動時自動執行的,並無專門的命令。可是因爲AOF的優先級更高,所以當AOF開啓時,Redis會優先載入AOF文件來恢復數據;只有當AOF關閉時,纔會在Redis服務器啓動時檢測RDB文件,並自動載入。服務器載入RDB文件期間處於阻塞狀態,直到載入完成爲止。

AOF持久化

因爲須要記錄Redis的每條寫命令,所以AOF不須要觸發,下面介紹AOF的執行流程。

AOF的執行流程包括:

  • 命令追加(append):將Redis的寫命令追加到緩衝區aof_buf;
  • 文件寫入(write)和文件同步(sync):根據不一樣的同步策略將aof_buf中的內容同步到硬盤;
  • 文件重寫(rewrite):按期重寫AOF文件,達到壓縮的目的。

文件重寫是指按期重寫AOF文件,減少AOF文件的體積。須要注意的是,AOF重寫是把Redis進程內的數據轉化爲寫命令,同步到新的AOF文件;不會對舊的AOF文件進行任何讀取、寫入操做! 

文件重寫之因此可以壓縮AOF文件,緣由在於:

  • 過時的數據再也不寫入文件
  • 無效的命令再也不寫入文件:若有些數據被重複設值(set mykey v1, set mykey v2)、有些數據被刪除了(sadd myset v1, del myset)等等
  • 多條命令能夠合併爲一個:

AOF重寫過程:

  1. Redis父進程首先判斷當前是否存在正在執行 bgsave/bgrewriteaof的子進程,若是存在則bgrewriteaof命令直接返回,若是存在bgsave命令則等bgsave執行完成後再執行。這個主要是基於性能方面的考慮。
  2. 父進程執行fork操做建立子進程,這個過程當中父進程是阻塞的
  3.  父進程fork後,bgrewriteaof命令返回」Background append only file rewrite started」信息並再也不阻塞父進程,並能夠響應其餘命令。Redis的全部寫命令依然寫入AOF緩衝區,並根據appendfsync策略同步到硬盤,保證原有AOF機制的正確。
  4.  因爲fork操做使用寫時複製技術,子進程只能共享fork操做時的內存數據。因爲父進程依然在響應命令,所以Redis使用AOF重寫緩衝區(圖中的aof_rewrite_buf)保存這部分數據,防止新AOF文件生成期間丟失這部分數據。也就是說,bgrewriteaof執行期間,Redis的寫命令同時追加到aof_buf和aof_rewirte_buf兩個緩衝區。
  5.  子進程根據內存快照,按照命令合併規則寫入到新的AOF文件。
  6.  子進程寫完新的AOF文件後,向父進程發信號,父進程更新統計信息,具體能夠經過info persistence查看。
  7.  父進程把AOF重寫緩衝區的數據寫入到新的AOF文件,這樣就保證了新AOF文件所保存的數據庫狀態和服務器當前狀態一致。
  8.  使用新的AOF文件替換老文件,完成AOF重寫。

 僞客戶端:由於Redis的命令只能在客戶端上下文中執行,而載入AOF文件時命令是直接從文件中讀取的,並非由客戶端發送;所以Redis服務器在載入AOF文件以前,會建立一個沒有網絡鏈接的客戶端,以後用它來執行AOF文件中的命令,命令執行的效果與帶網絡鏈接的客戶端徹底同樣。

RDB和AOF各有優缺點:

RDB持久化

  • 優勢:RDB文件緊湊,體積小,網絡傳輸快,適合全量複製;恢復速度比AOF快不少。固然,與AOF相比,RDB最重要的優勢之一是對性能的影響相對較小。
  • 缺點:RDB文件的致命缺點在於其數據快照的持久化方式決定了必然作不到實時持久化,而在數據愈來愈重要的今天,數據的大量丟失不少時候是沒法接受的,所以AOF持久化成爲主流。此外,RDB文件須要知足特定格式,兼容性差(如老版本的Redis不兼容新版本的RDB文件)。

AOF持久化

  • 與RDB持久化相對應,AOF的優勢在於支持秒級持久化、兼容性好,缺點是文件大、恢復速度慢、對性能影響大。

Redis單機的內存不能過大 :

  • 當面對請求的暴增,須要從庫擴容時,Redis內存過大會致使擴容時間太長;
  • 當主機宕機時,切換主機後須要掛載從庫,Redis內存過大致使掛載速度過慢;
  • 以及持久化過程當中的fork操做,下面詳細說明。

父進程經過fork操做能夠建立子進程,子進程建立後,父子進程共享代碼段,不共享進程的數據空間,可是子進程會得到父進程的數據空間的副本。在操做系統fork的實際實現中,基本都採用了寫時複製技術,即在父/子進程試圖修改數據空間以前,父子進程實際上共享數據空間;可是當父/子進程的任何一個試圖修改數據空間時,操做系統會爲修改的那一部分(內存的一頁)製做一個副本。雖然fork時,子進程不會複製父進程的數據空間,可是會複製內存頁表(頁表至關於內存的索引、目錄);父進程的數據空間越大,內存頁表越大,fork時複製耗時也會越多。

若是Redis單機內存達到了10GB,fork時耗時可能會達到百毫秒級別

相關文章
相關標籤/搜索