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