1. 程式人生 > >【大廠面試06期】談一談你對Redis持久化的理解?

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

Redis持久化是面試中經常會問到的問題,這裡主要通過對以下幾個問題進行分析,幫助大家瞭解Redis持久化的實現原理。 1.Redis持久化是什麼? 2.Redis持久化有哪些策略?各自的實現原理是怎麼樣的? 3.Redis的資料恢復策略是怎麼樣的? 4.Redis持久化策略該如何進行選擇? # 1.Redis持久化是什麼? 因為Redis是一個記憶體資料庫,資料儲存在記憶體中,一旦發生關機或者重啟,記憶體中的資料都會丟失,所以為了能夠重啟時恢復資料,Redis提供了持久化的機制,正常執行期間根據策略生成持久化檔案。在機器重啟後,可以根據根據持久化檔案恢復記憶體中的資料。Redis還為我們提供了持久化的機制。(雖然有主從同步,主機掛掉之後,可以讓從節點成為主節點,但是如果整個機房都發生停電,那麼主節點和從節點記憶體中的資料都會丟失,所以這也是持久化存在的意義。) # 2.Redis持久化有哪些策略? Redis持久化的策略主要有AOF持久化,RDB持久化,混合持久化。這是我自己總結的一個圖: ![](https://images.xiaozhuanlan.com/photo/2020/d346ae014766af6e92f853ef6dc3991f.png) ## AOF持久化 ![](https://images.xiaozhuanlan.com/photo/2020/3289c2b431d04cf7121bbb08be870905.png) #### 執行流程 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的實現中,也有用到,新增或者插入一個新元素時過程是,加鎖,對原陣列進行復制,然後新增新元素,然後替代舊陣列,解鎖) ![](https://images.xiaozhuanlan.com/photo/2020/5384c5eaaac8a1ba126bae957bae7149.png) ```java //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格式的增量資料,如下圖: ![](https://images.xiaozhuanlan.com/photo/2020/ee155f1840a324c39f1916d1803f9c4b.png) # 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檔案備份到其他地區的機房。