天天看點

Redis核心基于時間點的備份恢複和基于AOF日志的增量同步機制設計

11月30日雲栖社群線上教育訓練,雲栖社群請來了阿裡雲資深開發工程師夏德軍為大家帶來阿裡雲redis核心優化的分享。本文從兩大方面介紹阿裡雲redis服務,一是redis核心支援基于時間點的備份恢複,一是redis基于aof日志的增量同步機制設計,并分别通過假設場景,詳細的分析了備份恢複流程和aof psync流程。一起來了解下吧。

<a href="https://yq.aliyun.com/edu/lesson/play/420">直播視訊回顧</a>

<a></a>

redis核心支援基于時間點的備份恢複

redis記憶體資料庫,須有一種機制能夠把記憶體中的資料持久化到硬碟上,再将硬碟中資料備份到備份系統中,才能去做恢複。redis原生的持久化機制包括rdb持久化和aof持久化兩種。

<b>rdb</b><b>持久化</b>

rdb持久化觸發方式有兩種:

手動觸發:執行bgsave指令;

自動觸發:配置save選項,在指定時間内發生指定次數的key修改,自動進行背景rdb save。

rdb持久化流程如下:

Redis核心基于時間點的備份恢複和基于AOF日志的增量同步機制設計

在做rdb save時需要fork一個子程序,每次rdb save生成一個對應時間點的記憶體快照檔案。

<b>aof</b><b>持久化</b>

<b></b>

Redis核心基于時間點的備份恢複和基于AOF日志的增量同步機制設計

配置appendonly選項,可以動态開關;

和rdb持久化不太一樣的是,每一次的寫操作指令都會追加到aof檔案中,根據配置的appendfsync選項不同,會有不同aof檔案重新整理政策;

redis通過aof rewrite的方式來“緊湊”aof,解決持續追加導緻aof檔案過大的問題。

<b>目前備份恢複方式</b>

現在redis核心能夠支援的備份方式有兩種:

1. 備份rdb:執行bgsave指令 -&gt; fork子程序 -&gt; 生成rdb檔案 -&gt; 備份rdb檔案;

2. 備份aof:根據aof大小判斷是否需要先做一次aof rewrite,備份aof,aof rewrite同樣需要fork子程序。

兩種方式都是備份一個全量資料檔案,通常不會選擇備份aof,rdb格式更為緊湊,備份開銷更小。

恢複時,建立執行個體,從備份系統拉取全量的aof或rdb檔案到執行個體資料目錄,恢複資料。

目前核心備份恢複方式隻能做全量資料的備份和恢複,fork子程序生成全量快照的方式開銷比較大。

<b>應用場景舉例</b>

Redis核心基于時間點的備份恢複和基于AOF日志的增量同步機制設計

如果能夠不管幾點上線,都能恢複redis資料到那個時間點,場景中的問題将不再存在。

<b>redis</b><b>基于時間點的備份恢複</b>

Redis核心基于時間點的備份恢複和基于AOF日志的增量同步機制設計

我們的目标就是要恢複資料到任意時間點,我們做了基于aof改造的增量日志,禁用aof rewrite,日志按照配置大小自動切分。除了每個指令執行的曆史日志要儲存下來,還需要添加一些額外的資訊,比如:

根據系統時間戳命名aof,aof中除了包含原有的redis協定格式的指令(oplog),每個oplog還對應了一個header;

header中包含多個字段,其中timestamp記錄了指令執行時的時間戳,基于任意時間點恢複時就依賴于該資訊;

server_id、opid和dbid是為後面講的主從同步優化所設計,reserved為保留字段;

oplog header以二進制的形式用redis指令封裝,保持aof協定格式不變。

<b>aof</b><b>日志管理</b>

Redis核心基于時間點的備份恢複和基于AOF日志的增量同步機制設計

aof會按固定大小做切分,我們需要将aof日志進行管理,主要有以下三點:

1. 使用aof index索引檔案記錄redis目前維護的aof檔案;

2. 使用專門的清理指令删除aof,同時維護aof index檔案的正确性;

3. bgsave完成時需要記錄rdb檔案對應的aof日志名和檔案寫入偏移,這個資訊儲存在rdb index檔案中。

<b>本地定時</b><b>bgsave</b>

Redis核心基于時間點的備份恢複和基于AOF日志的增量同步機制設計

我們也需要本地定時bgsave機制,aof會越變越大,仍然需要某種方式來“緊湊”aof日志,比如aof rewrite方式複合式程序重寫aof日志,redis執行個體在本地也需要儲存一份持久化的全量資料,重新開機時從aof日志恢複資料是不現實的。

對此,我們引入cron bgsave,當aof日志的增長量滿足一定條件時,觸發bgsave,生成rdb檔案,該rdb檔案對應的時間點之前的aof日志在本地都是不需要的,隻要已經上傳到備份系統,即可删除。

<b>備份恢複流程</b>

Redis核心基于時間點的備份恢複和基于AOF日志的增量同步機制設計

時間點備份恢複的流程如圖所示,左側假如有一個redis-server執行個體,追加寫aof6檔案,接着實時備份aof日志,上傳到備份空間中,同時也有定時全量備份,每一個rdb檔案會對應一個rdb檔案寫入偏移,每個aof檔案會有一個起始時間;右側指定要恢複的時間點,redis順序加載rdb、aof到指定地點。

備份和原來全量備份的差別在于,需要實時持續地把redis生成的aof增量日志不斷的備份下來,我們也要有删除的機制,比如儲存一個星期或一個月的情況。

redis基于aof日志的增量同步機制設計

redis原生同步機制主要有兩種,全量同步和增量同步。

<b>全量同步</b>

Redis核心基于時間點的備份恢複和基于AOF日志的增量同步機制設計

全量同步代價非常大,全量同步意味着有一個空的redis執行個體挂上來後,master需要做一次bgsave,還需要網絡傳輸rdb檔案,發送給slave,大量占用主庫帶寬、cpu資源,大記憶體時fork會導緻redis hang住,雖然fork是寫實複制,但需要copy頁表,如果記憶體比較大,頁表也會比較大。

<b>增量同步</b>

redis在2.8中引入了增量同步機制,是為了解決網絡偶爾的抖動導緻需要全量同步的問題。

增量同步機制實作比較簡單,master和slave之間維護一個一緻的同步offset,此外,master還在記憶體裡面維護了一個同步buffer,儲存了最近一段時間内的更新指令日志,當slave斷鍊重連時,會根據自己的同步偏移從master的同步buffer直接拉取資料,完成同步。

理想情況下,這個流程同步很快,代價很低,但是當網絡斷開時間較長時,或主庫寫入非常大時,同步buffer會将曆史資料覆寫掉,仍然需要全量同步。而且,現有的增量同步機制,也沒有解決一主多從場景下,主從切換,其他從需要從新主全量同步的問題。

有不少使用redis的業務的重要性很高,譬如說金融業務,都有跨機房,甚至跨地域災備的需求,而跨機房或跨地域的網絡品質往往很難保證。

假設有一天,redis業務的杭州主機房和深圳的備機房之間的光纖被人挖斷了,網絡中斷數小時,恢複後,深圳機房所有的備執行個體同時發起全量同步,導緻的後果很可能是:主庫所在機器帶寬、cpu打滿,甚至有些機器會直接oom,而備又還沒有完成同步,後果可能是災難性的,對此,我們該如何處理呢?

<b>基于</b><b>aof</b><b>日志的增量同步(</b><b>aof psync</b><b>)</b>

由于現有機制存在的問題,以及真實場景中業務需求的推動,我們想到不依賴于具體記憶體的buffer,用增量日志做增量同步,那麼,基于aof日志的增量同步是怎麼實作的呢?具體原了解釋如下:

當主從同步斷開,需要拉取增量時,會先選擇嘗試基于同步偏移從主庫同步buffer擷取資料,如果同步buffer缺少增量資料,會開始從aof日志擷取增量;

aof psync以aof日志中的opid作為同步基準,slave繼承從主庫同步過來的opid,并記錄在自己的aof日志中;

使用opid作為同步基準是因為對于帶過期時間的指令,redis寫aof和寫同步buffer的位元組序列不一樣,redis在寫aof時會轉換設定過期時間的指令為pexpireat;

aof日志中的dbid,決定了aof中的每條指令寫入的db是哪個。aof psync同步完成後,會同步master和slave間的同步偏移。

<b>aof psync</b><b>流程</b>

Redis核心基于時間點的備份恢複和基于AOF日志的增量同步機制設計

1. master收到slave的aof psync請求後,會使用背景線程來查找offset,避免server hang住;

2. 根據slave發送的opid查找到起始的aof檔案後會異步的通知主線程;

3. 主線程會異步發送一個或多個aof檔案給slave,發送完成後,需要同步aof buffer;

4. 如果slave給定的opid在master的aof中不存在(aof可能被異常删除),會發起全量同步。

阿裡雲redis服務

Redis核心基于時間點的備份恢複和基于AOF日志的增量同步機制設計

與其它redis對比,阿裡雲redis服務具備高可靠、高性能、高擴充、易用的特點。