【大廠面試06期】談一談你對Redis持久化的理解?

NotFound9發表於2020-06-10

Redis持久化是面試中經常會問到的問題,這裡主要通過對以下幾個問題進行分析,幫助大家瞭解Redis持久化的實現原理。

1.Redis持久化是什麼?

2.Redis持久化有哪些策略?各自的實現原理是怎麼樣的?

3.Redis的資料恢復策略是怎麼樣的?

4.Redis持久化策略該如何進行選擇?

1.Redis持久化是什麼?

因為Redis是一個記憶體資料庫,資料儲存在記憶體中,一旦發生關機或者重啟,記憶體中的資料都會丟失,所以為了能夠重啟時恢復資料,Redis提供了持久化的機制,正常執行期間根據策略生成持久化檔案。在機器重啟後,可以根據根據持久化檔案恢復記憶體中的資料。Redis還為我們提供了持久化的機制。(雖然有主從同步,主機掛掉之後,可以讓從節點成為主節點,但是如果整個機房都發生停電,那麼主節點和從節點記憶體中的資料都會丟失,所以這也是持久化存在的意義。)

2.Redis持久化有哪些策略?

Redis持久化的策略主要有AOF持久化,RDB持久化,混合持久化。這是我自己總結的一個圖:

AOF持久化

執行流程

AOF持久化主要是Redis在修改相關的命令後,將命令新增到aof_buf快取區的末尾,然後在每次事件迴圈結束時,

根據appendfsync的配置:

  • appendfsync = always 每條修改命令都會更新到磁碟上的AOF檔案, 最多隻會丟失當前正在寫入的命令

  • appendfsync = everysec 每秒更新到磁碟上的AOF檔案一次, 最多丟失2秒的資料(因為執行fsync命令刷盤也需要時間,下面會解釋)

  • appendfsync = no 不自動更新到磁碟上的AOF檔案,由作業系統來決定何時刷盤(linux 貌似大部分預設是 30s)。可能會丟失刷盤之前的寫入資料。

(基於效能考慮一般生產環境的配置都是everysec)

(aof_buf是Redis中的SDS結構,可以理解為是一個字串,只是對C語言的字串做了一些優化,每次將新執行的更新命令新增到字串末尾。)

怎麼防止AOF檔案越來越大?

為了防止AOF檔案越來越大,可以通過執行BGREWRITEAOF命令,會fork子程式出來,讀取當前資料庫的鍵值對資訊,生成所需的寫命令,寫入新的AOF檔案。在生成期間,父程式繼續正常處理請求,執行修改命令後,不僅會將命令寫入aof_buf緩衝區,還會寫入重寫aof_buf緩衝區。當新的AOF檔案生成完畢後,子程式父程式傳送訊號,父程式將重寫aof_buf緩衝區的修改命令寫入新的AOF檔案,寫入完畢後,對新的AOF檔案進行改名,原子地(atomic)地替換舊的AOF檔案。

什麼是AOF檔案追加阻塞?

修改命令新增到aof_buf之後,如果配置是everysec那麼會每秒執行fsync操作,呼叫write寫入磁碟一次,但是如果硬碟負載過高,fsync操作可能會超過1s,Redis主執行緒持續高速向aof_buf寫入命令,硬碟的負載可能會越來越大,IO資源消耗更快,所以Redis的處理邏輯是會對比上次fsync成功的時間,如果超過2s,則主執行緒阻塞直到fsync同步完成,所以最多可能丟失2s的資料,而不是1s。

RDB持久化

RDB持久化指的是在滿足一定的觸發條件時(在一個的時間間隔內執行修改命令達到一定的數量,或者手動執行SAVE和BGSAVE命令),對這個時間點的資料庫所有鍵值對資訊生成一個壓縮檔案dump.rdb,然後將舊的刪除,進行替換。

執行流程

實現原理是fork一個子程式,然後對鍵值對進行遍歷,生成rdb檔案,在生成過程中,父程式會繼續處理客戶端傳送的請求,當父程式要對資料進行修改時,會對相關的記憶體頁進行拷貝,修改的是拷貝後的資料。(也就是COPY ON WRITE,寫時複製技術,就是當多個呼叫者同時請求同一個資源,如記憶體或磁碟上的資料儲存,他們會共用同一個指向資源的指標,指向相同的資源,只有當一個呼叫者試圖修改資源的內容時,系統才會真正複製一份專用副本給這個呼叫者,其他呼叫者還是使用最初的資源,在CopyOnWriteArrayList的實現中,也有用到,新增或者插入一個新元素時過程是,加鎖,對原陣列進行復制,然後新增新元素,然後替代舊陣列,解鎖)

	//CopyOnWriteArrayList的新增元素的方法
public boolean add(E e) {
  final ReentrantLock lock = this.lock;
  lock.lock();
  try {
    Object[] elements = getArray();
    int len = elements.length;
    Object[] newElements = Arrays.copyOf(elements, len + 1);
    newElements[len] = e;
    setArray(newElements);
    return true;
  } finally {
    lock.unlock();
  }
}

混合持久化(Redis4.0+)

執行流程

混合持久化同樣也是通過bgrewriteaof命令完成的,不同的是當開啟混合持久化時,fork出的子程式先將當前記憶體中的鍵值對資訊全量的以RDB方式寫入aof檔案,然後在將重寫緩衝區的增量命令以AOF方式寫入到檔案,寫入完成後通知主程式更新統計資訊,並將新的含有RDB格式和AOF格式的AOF檔案替換舊的的AOF檔案。簡單的說:新的AOF檔案前半段是RDB格式的全量資料後半段是AOF格式的增量資料,如下圖:

3.Redis的資料恢復策略是怎麼樣的?

1.如果配置了混合持久化,那麼根據混合持久化檔案進行恢復資料。(Redis4.0+)

2.只配置 AOF ,重啟時載入 AOF 檔案恢復資料。

3.同時配置了 RDB 和 AOF ,啟動時只載入 AOF檔案恢復資料,如果AOF檔案損壞,那麼根據RDB檔案恢復資料。

4.只配置 RDB,啟動時載入RDB持久化檔案恢復資料。

4.Redis持久化策略該如何進行選擇?

(因為混合持久化是Redis 4.0之後支援的,目前一般生成環境使用的Redis版本可能都還較低,所以這裡的策略選擇主要是針對AOF持久和RDB持久化進行技術選型。)

以下是幾種持久化方案選擇的場景:

1.不需要考慮資料丟失的情況

那麼不需要考慮持久化。

2.單機例項情況下

可以接受丟失十幾分鍾及更長時間的資料,可以選擇RDB持久化,對效能影響小,如果只能接受秒級的資料丟失,只能選擇AOF持久化。

3.在主從環境下

因為主伺服器在執行修改命令後,會將命令傳送給從伺服器,從服務進行執行後,與主伺服器保持資料同步,實現資料熱備份,在master宕掉後繼續提供服務。同時也可以進行讀寫分離,分擔Redis的讀請求。

那麼在從伺服器進行資料熱備份的情況下,是否還需要持久化呢?

需要持久化,因為不進行持久化,主伺服器,從伺服器同時出現故障時,會導致資料丟失。(例如:機房全部機器斷電)。如果系統中有自動拉起機制(即檢測到服務停止後重啟該服務)將master自動重啟,由於沒有持久化檔案,那麼master重啟後資料是空的,slave同步資料也變成了空的。應儘量避免“自動拉起機制”和“不做持久化”同時出現。

所以一般可以採用以下方案:

主伺服器不開啟持久化,使得主伺服器效能更好。

從伺服器開啟AOF持久化,關閉RDB持久化,並且定時對AOF檔案進行備份,以及在凌晨執行bgaofrewrite命令來進行AOF檔案重寫,減小AOF檔案大小。(當然如果對資料丟失容忍度高也可以開啟RDB持久化,關閉AOF持久化)

4.異地災備

一般性的故障(停電,關機)不會影響到磁碟,但是一些災難性的故障(地震,洪水)會影響到磁碟,所以需要定時把單機上或從伺服器上的AOF檔案,RDB檔案備份到其他地區的機房。

相關文章