天天看點

談一談你對Redis持久化的了解?1.Redis持久化是什麼?2.Redis持久化有哪些政策?3.Redis的資料恢複政策是怎麼樣的?4.Redis持久化政策該如何進行選擇?

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

1.Redis持久化是什麼?

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

3.Redis的資料恢複政策是怎麼樣的?

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

1.Redis持久化是什麼?

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

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

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

談一談你對Redis持久化的了解?1.Redis持久化是什麼?2.Redis持久化有哪些政策?3.Redis的資料恢複政策是怎麼樣的?4.Redis持久化政策該如何進行選擇?

AOF持久化

談一談你對Redis持久化的了解?1.Redis持久化是什麼?2.Redis持久化有哪些政策?3.Redis的資料恢複政策是怎麼樣的?4.Redis持久化政策該如何進行選擇?

執行流程

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格式的增量資料,如下圖:

談一談你對Redis持久化的了解?1.Redis持久化是什麼?2.Redis持久化有哪些政策?3.Redis的資料恢複政策是怎麼樣的?4.Redis持久化政策該如何進行選擇?

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檔案備份到其他地區的機房。

繼續閱讀