1. 程式人生 > >詳細分析Redis的持久化操作——RDB與AOF

詳細分析Redis的持久化操作——RDB與AOF

# 一、前言   由於疫情的原因,學校還沒有開學,這也就讓我有了很多的時間。趁著時間比較多,我終於可以開始學習那些之前一直想學的技術了。最近這幾天開始學習``Redis``,買了本``《Redis實戰》``,看到了第四章,前三章都是講一些``Redis``的基本使用以及命令,第四章才開始涉及到原理相關的內容。``《Redis實戰》``的第四章涉及到了``Redis``的持久化、主從複製以及事務等內容,我個人認為這些應該屬於``Redis``中比較重要的部分,也是面試的常考內容。這篇部落格就來記錄一下``Redis``的持久化機制。
# 二、正文 ## 2.1 為什麼需要持久化   學習過``Redis``的應該都知道,``Redis``與``MySQL``等關係型資料庫不同,它的資料不是儲存在硬碟中,而是存放在記憶體,所以``Redis``的速度非常快。而這也就會造成一個問題:**電腦如果宕機,或者由於某些原因需要重啟,此時記憶體中的資料就會丟失**。``Redis``既然把資料存放在記憶體,自然也就無法避免這個問題。所以,為了在電腦重啟後,能夠恢復原來的資料,``Redis``就需要提供持久化的機制,將``Redis``資料庫儲存在記憶體中的資料,在磁碟中進行備份,也就是持久化。而當``Redis``重啟後,去磁碟中將持久化的資料重新讀取到記憶體,便能避免資料的丟失。
## 2.2 Redis的持久化方式   ``Redis``提供了兩種持久化的方式,分別是: - 快照持久化; - AOF持久化;   下面我就來詳細地介紹這兩種持久化的方式。
## 2.3 快照持久化(RDB)   快照持久化也就做``RDB``持久化。快照持久化的實現方式簡單來說就是:**Redis將當前記憶體中儲存的資料寫入到一個檔案中,將這個檔案作為Redis當前的一個快照,儲存在磁碟中。當Redis重啟時,將這個快照檔案中儲存的內容載入進記憶體,即可恢復Redis之前的狀態**。預設情況下,``Redis``將快照儲存在一個叫做``dump.rdb``的檔案中,我們也可以在配置檔案中,通過``dbfilename``選項來設定快照檔案的名稱;預設情況下快照檔案就儲存在``Redis``的安裝目錄下,我們可以在配置檔案中,通過``dir``選項來配置快照檔案的儲存路徑(其實也是AOF的路徑)。
## 2.4 執行快照持久化的方式   執行快照持久化有兩種方式:  **2.4.1 使用配置檔案**   第一種方式就是在``Redis``的配置檔案中(``windows``下這個檔案叫``redis.windows-service.conf``)加上``save``配置項,比如像下面這樣: > **save 60 100**:在配置檔案中加上這一條的意思是,Redis會每60秒檢查一次,若在這60秒中,Redis資料庫執行了100次以上的寫操作,那Redis就會生成一個快照檔案,替換原來的快照檔案;若不滿足這個條件,則不生成快照,繼續等待60秒;   我們可以根據自己的需求,調整每次等待的時間,以及對寫操作次數的要求。而且,我們可以在配置檔案中,新增多個``save``選項,比如一個``save 60 100``,一個``save 5 10``,則``Redis``每``5``秒以及每``100``秒都會判斷一次。需要注意的是,我們不應該讓生成快照太過頻繁,因為這是一個比較消耗資源的工作,會降低``Redis``的響應速度。
 **2.4.2 使用指令**   執行快照持久化的第二個方法就是使用``Redis``指令,``Redis``提供了兩個指令來請求伺服器進行快照持久化,這兩個指令分別是**SAVE**和**BGSAVE**。  **(a)BGSAVE指令**   ``BGSAVE``的執行流程如下: 1. ``Redis``呼叫系統的``fork()``,創建出一個子程序; 2. 子程序將當前``Redis``中的資料,寫入到一個臨時檔案中;同時父程序不受影響,繼續執行客戶端的請求; 3. 子程序將所有的資料寫入到了臨時檔案後,於是使用這個檔案替換原來的快照檔案(預設是``dump.rdb``);   值得一提的是,通過配置檔案執行快照持久化的方式,實際上就是``Redis``在判斷滿足條件時,呼叫``BGSAVE``指令來實現的。  **(b)SAVE指令**   ``SAVE``指令生成快照的方式與``BGSAVE``不同,``Redis``執行``SAVE``指令時,不會建立一個子程序,非同步的生成快照檔案,而是直接使用``Redis``當前程序。執行``SAVE``指令在建立快照的過程中,``Redis``伺服器會阻塞所有的``Redis``客戶端,直到快照生成完畢,並更新到磁碟之後,才會繼續執行客戶端發來的增刪改查的指令。   當然,這個指令一般很少使用,因為會阻塞客戶端,造成停頓。但是實際上,這個指令的執行效率一般比``BGSAVE``更高,因為不需要建立子程序,而且在這個過程中,其他操作被阻塞,``Redis``伺服器一心一意地生成快照。在``《Redis實戰》``中,作者提到了使用``SAVE``指令的一個案例: >   當前伺服器的Redis資料庫中儲存了大量資料,使用BGSAVE指令生成快照會非常的耗時 ,而且由於所剩記憶體不多,甚至無法建立子程序,於是作者編寫了一個shell指令碼,這個指令碼的內容就是讓伺服器每天凌晨3點,執行SAVE命令,生成快照,這樣就不需要建立子程序,而且由於是凌晨三點,使用者較少,SAVE的阻塞機制也不會有太大的影響。
## 2.5 快照持久化的優缺點 **(1)優點:** 1. 快照的``rdb``檔案是一個經過壓縮的緊湊檔案,它儲存了``Redis``在某個時間點上的資料集,這個檔案非常適合用來備份。我們可以儲存``Redis``伺服器在不同時間點上的``rbd``檔案,比如說一小時儲存一次,一個月儲存一次,這樣就可以在遇到問題或有特殊需求時,將``Redis``恢復到某一個時間點; 2. RDB非常適用於災難恢復(disaster recovery):它只有一個檔案,並且內容都非常緊湊,可以(在加密後)將它傳送到別的伺服器上; 3. RDB 可以最大化 Redis 的效能:父程序在儲存 ``RDB`` 檔案時唯一要做的就是 `fork` 出一個子程序,然後這個子程序就會處理接下來的所有儲存工作,父程序無須執行任何磁碟`` I/O ``操作,所以不會影響父程序處理客戶端的請求; 4. ``RDB`` 在恢復大資料集時的速度比`` AOF`` 的恢復速度要快。 **(2)缺點**: 1. 當我們的伺服器發生異常,導致停機時,那我們將會丟失最後一次建立快照後,所作的所有寫操作,因為這些操作發生在記憶體中,還沒來得及同步到磁碟。比如我們在配置檔案中配置每``5``分鐘生成一次快照,那當系統發生故障導致宕機時,我們將會丟失好幾分鐘內的操作; 2. 每次執行``BGSAVE``建立快照,都需要先創建出一個子程序,再由子程序執行後續操作,當記憶體中資料較大時,建立一個子程序將會非常耗時,造成伺服器等待數毫秒,甚至達到一秒,影響使用者體驗。而且從這一點也可以說明,我們不能通過提高生成快照的頻率,來解決第一個缺點;
## 2.6 AOF持久化   ``AOF``全稱為只追加檔案(append-only file),它的實現方式簡單來說就是:**AOF持久化機制,會將Redis執行的所有寫指令,追加到到AOF的末尾,當伺服器發生宕機,或者由於某些原因需要重啟時,在重啟後,讀取AOF檔案,重新執行其中記錄的寫操作,以此達到恢復資料的目的。**   ``AOF``持久化機制預設是關閉的,我們可以在配置檔案中,配置**appendonly yes**來開啟。同時我們也可以通過配置**appendfsync**,控制寫操作追加到``AOF``中的頻率,它有如下三種選項: - **always**:``Redis``每次執行寫指令,都會立即將這個寫指令同步到``AOF``中;使用這個選項時,``Redis``發生異常,則最多隻會丟失一次寫操作(也就是在同步的過程中宕機,沒同步完成),但是這也會導致``Redis``的響應速度變慢,因為此選項會造成頻繁的``IO``操作; - **everysec(預設)**:每秒同步一次,使用此選項,速度足夠快,而且發生宕機時也只會丟失``1s``內的寫操作; - **no**:讓作業系統來決定什麼時候進行同步,這個選項速度更快,但是不安全,宕機時丟失資料的多少是不確定的;   推薦(也是預設),使用第二個選項``everysec``,每秒進行一次同步,因為這個選項兼顧了速度與安全性,而第一個選項太慢,第三個選項無法保證安全性。
## 2.7 AOF的重寫機制   ``AOF``持久化的執行機制就是,不斷地將寫指令追加到``AOF``的末尾,這樣就會導致``AOF``越來越大。為了解決``AOF``越來越大的問題,``Redis``實現了一種優化機制——**AOF重寫**。當``Redis``檢查到``AOF``已經很大時,就會觸發重寫機制,優化其中的內容,將它優化為能夠得到相同結果的最小的指令集合。   比如說,我們在``Redis``中使用``SET``指令建立了一個``String``型別的資料,最後使用``DEL``指令將它刪除了。如果開啟了``AOF``持久化,那麼則``AOF``中,將會記錄這條``SET``和``DEL``指令。但是,在執行重寫的過程中,這個String最後被刪除了,那麼``Redis``就不會將這兩條指令加入新的``AOF``中,因為已經被刪除的資料,不需要恢復。再比如說,我們使用``Redis``維護了一個計數器``cnt``,我們使用了``100``次``INCR``指令,讓``cnt``自增到了``100``,而``Redis``重寫``AOF``時,可以將這``100``條``incr``修改為一條``SET``指令,直接將``cnt``設定為``100``,而不是保留``100``條``INCR``。   經過了重寫後,``AOF``的大小將會大大減小,而且也去除了不必要的操作,優化了恢復資料的指令集。而``AOF``重寫的過程與生成一個快照檔案類似,如下: 1. ``Redis``執行``fock()``,建立一個子程序用來執行後續操作,而父程序繼續處理髮送到``Redis``的執行請求; 2. 子程序重寫舊``AOF``檔案,將重寫後的內容寫入到一個臨時檔案; 3. 如果這個過程中,有新的寫指令到達,那麼``Redis``會將這些寫指令依舊追加到舊的``AOF``中,同時也會將這些指令加入到記憶體的一個緩衝區中。這樣做的目的是,如果伺服器發生異常,``AOF``重寫失敗,這些指令依然能夠儲存在舊``AOF``,不會丟失; 4. 當子程序完成重寫工作時,它給父程序傳送一個訊號,父程序在接收到訊號之後,將記憶體快取中的所有寫指令追加到新 ``AOF`` 檔案的末尾; 5. 使用新``AOF``替換舊的``AOF``,這之後執行的所有寫指令都將追加到新的``AOF``中;
## 2.8 AOF的錯誤處理   ``AOF``檔案是有可能發生錯誤的,比如上面提過,如果當前``Redis``正在向``AOF``中追加一個寫指令,但是此時伺服器宕機,那麼這個存入``AOF``中的這個寫指令就是不完整的,也就是``AOF``出現了錯誤。如果停機造成了 ``AOF`` 檔案出錯(corrupt), 那麼 ``Redis`` 在重啟時會拒絕載入這個 ``AOF`` 檔案, 從而確保資料的一致性不會被破壞。   那麼,當``AOF``發生了錯誤,應該如何處理呢?我們可以使用如下命令: > **redis-check-aof --fix**:redis-check-aof用來檢測AOF是否存在錯誤,如果指定了--fix引數,那麼Redis在檢測到AOF的錯誤後,會對AOF進行修復。   ``redis-check-aof``修復``AOF``的方式非常簡單:**掃描AOF檔案,尋找其中不正確或不完整的指令,當發現第一個出錯的指令後,就將這個指令以及這之後的所有指令刪除**。為什麼需要刪除出錯指令之後的所有指令呢?因為當一條指令出錯,很有可能影響到後續的操作,導致後續操作的都是髒資料,而``Redis``無法檢測哪些指令是受到影響的,所以為了保險起見,就將後續指令全部刪除。不過不用擔心,因為在大多數情況下,出錯的都是``AOF``最末尾的指令。
## 2.9 AOF的優缺點 **(1)優點:** 1. 使用 ``AOF`` 持久化會讓 ``Redis`` 變得非常耐久,意思就是說,當發生異常導致需要重啟伺服器時,只會丟失很少的一部分資料,因為``AOF``持久化預設``1s``同步一次,也就是說,``Redis``最多隻會丟失``1s``中所做的修改; 2. ``Redis ``可以在 ``AOF`` 檔案體積變得過大時,自動地在後臺對 ``AOF`` 進行重寫: 重寫後的新 ``AOF`` 檔案包含了恢復當前資料集所需的最小命令集合。 3. ``AOF`` 檔案有序地儲存了對資料庫執行的所有寫入操作, 這些寫入操作以 ``Redis`` 協議的格式儲存, 因此 ``AOF`` 檔案的內容非常容易被人讀懂, 對檔案進行分析(``parse``)也很輕鬆。 4. ``AOF`` 檔案是一個只進行追加操作的日誌檔案(``append only log``), 因此對 ``AOF`` 檔案的寫入不需要進行 `seek` , 即使日誌因為某些原因而包含了未寫入完整的命令(比如寫入時磁碟已滿,寫入中途停機,等等), `redis-check-aof` 工具也可以輕易地修復這種問題。 **(2)缺點:** 1. 對於相同的資料集來說,``AOF`` 檔案的體積通常要大於快照``RDB``檔案的體積大,因為``RDB``只儲存資料,而``AOF``中的寫指令,既包含指令,也包含資料; 2. 如果``AOF``體積太大,那麼恢復資料將要花費較長的時間,因為需要重做指令;
## 2.10 選擇快照還是AOF?   說到這裡,可能就有人要問了,在實際生產中,我們應該使用快照持久化還是``AOF``持久化呢? 1、如果希望自己的資料庫有很高的安全性,則應該兩者同時使用,``AOF``用作精確記錄,而快照用作資料備份,前面也說過,快照非常適合用來做資料備份,因為它只儲存資料庫中的資料,並且經過了壓縮,而且它恢復``Redis``資料的速度一般要快於``AOF``; 2、如果可以容忍一段時間內的資料丟失,則可以考慮只使用快照持久化,減小伺服器的開銷;   值得一提的是,如果我們同時開啟了快照持久化和``AOF``持久化,``Redis``在重啟後,會優先選擇``AOF``來恢復資料,因為一般情況下,``AOF``能夠更加完整地恢復資料。
# 三、總結   快照持久化和``AOF``持久化各有優劣,在實際生產環境中,我們一般是兩者配合使用,快照持久化消耗較低,而且適合用於備份,但是會丟失一段時間的資料;``AOF``持久化更加地耐久,可靠性更高,但是開銷可能相對較高。以上就對``Redis``的持久化機制做了一個比較詳細的介紹,相信看完只後,對``Redis``會有一個更加深入的理解。
# 四、參考 - 《Redis實戰》 - [Redis官方文件——持久化](http://doc.redisfans.com/topic/persisten