揭開RedisShake的祕密

<meta name="source" content="lake">

簡介: RedisShake的設計思想,底層實現以及豐富的應用場景介紹。本文爲直播內容文字整理,看直播回放,請點擊文首鏈接~

查看精彩回放:https://developer.aliyun.com/live/145188

內容簡要:

一、背景

二、RedisShake基本原理

三、應用場景

一、背景

(一)RedisShake能做什麼

如上圖所示,RedisShake主要能做的事情有數據遷移、版本變更、架構變更與容災等。現在大部分的數據庫都支持集羣版的數據,也就是說一個邏輯單元中可以有很多個DB,不同節點之間可以通過複製的方式來實現數據同步。

從廣義上來說,遷移屬於同步中的一種模式,同步更加側重於增量,遷移更加側重於全量。

遷移有很多場景,比如架構變更,像Redis由主從版遷移成集羣版,或者從2個分片遷移到3個分片,或者數據庫的升級遷移,從4.0版本升級到5.0,RedisShake也做了降級遷移的支持。

數據遷移可以是上雲遷移,比如說用戶自建的Redis數據庫遷移上雲,或者雲上的數據庫遷移到雲下等,RedisShake也支持多雲廠商之間的數據遷移。

容災主要是防止不可控事情的發生,例如機房斷電着火或者是地震等,使得集羣中超過半數的節點不能提供正常服務,這時候備份就可以代替原數據庫對外提供服務,因此異地容災非常有必要。

(二)Redis持久化

Redis有兩種持久化機制:RDB和AOF。

RDB是一個經過壓縮的二進制文件,通過保存Redis數據庫中的鍵值來記錄數據庫狀態。

AOF通過保存Redis服務器所執行的寫命令來記錄數據庫狀態,例如:

RDB和AOF文件可以恢復成一個數據庫,繼續提供服務。

  • Replication

複製:包括全量數據的複製和增量數據的複製。

同步操作:將從服務器的數據庫狀態更新至主服務器當前所處的數據庫狀態。Master生成RDB文件,Slave載入。

命令傳播操作:主服務器的數據庫狀態被修改,導致主從服務器的數據庫狀態出現不一致時,讓主從服務器的數據庫重新回到一致狀態。 Master執行的寫命令傳送到Slave。

(三)Redis主從複製

在分佈式環境中,數據副本 (Replica) 和複製 (Replication) 作爲提升系統可用性和讀寫性能的有效手段被大量應用系統設計中,Redis也不例外。

在2.8版本之前,主從同步是通過SYNC命令進行的,在2.8版本之後升級到PSYNC命令,下面分別展開介紹。

(四)主從複製 - SYNC

在發送slaveof ip port的時候,它首先會創建兩項主服務器套接字的連接,接着向主服務器發送ping命令,目的在於檢查套接字的讀寫狀態,或者是檢查主服務器是否能夠正常地處理命令。

接下來進行身份驗證,然後發送端口信息,最後發送SYNC命令,執行同步操作。

在主Master接收到SYNC命令之後,它會執行bgsave在後臺生成一個RDB文件,並且使用一個緩衝區記錄從現在開始執行所有寫命令。當bgsave生成的RDB文件完成了之後,它就發送給從服務器去進行載入。在更新狀態完成之後,Master再將記錄在緩衝區裏面的新命令發送給從服務器,這樣從服務器進行執行,主從服務器就保持了一致狀態。

  • SYNC****缺陷

從服務器到主服務器的複製可以分爲兩種情況,一種就是初次複製,一種就是斷線後的重複制。初次複製就是進行建立連接,然後進行全量和增量的同步,它的SYNC可以很好地完成任務。

但對於斷線後的重複制,處於命令傳播階段的主從服務器因爲網絡原因而中斷又重連,會再次發送SYNC命令做全量+增量同步,效率較低。

SYNC命令是一個非常耗費資源的操作,資源包括CPU、內存、磁盤、寬帶、流量等。

(五)主從複製 - PSYNC

爲了解決SYNC在處理斷線重複制時候的低效問題,Redis從2.8版本之後開始使用PSYNC命令,它支持完整重同步和部分重同步。

完整重同步和SYNC一樣,部分重同步就是在處理斷線重新鏈接之後,主節點只向從節點發送鏈接斷開期間的寫命令,它的實現基於以下三部分:

1)複製偏移量(replication offset)

n Master:每次向Slave發送n個字節數據時,就會將自己的offset+n;

n Slave:每次收到Master發送來的n個字節數據時,就會將自己的offset+n;

n 如果主從服務器處於一致狀態,那麼Master和Slave的Offset總是相同的。

2)複製積壓緩衝區(replication backlog)

  • Master上維護的每一個固定大小(fixed-size)的FIFO隊列,保存着一部分最近傳播的寫命令。

  • Master進行命令傳播時,不僅會將命令發送給所有Slave,還會將寫命令入隊到複製積壓緩衝區。

  • 複製積壓緩衝區會爲隊列中的每個字節記錄相應的複製偏移量;

  • Slave重連Master時,會通過PSYNC命令將自己的Offset發送給Master,如果Slave的Offset與Master的Offset不相等,並且Slave的Offset偏移量之後的數據仍存在於replication backlog中,那麼Master將對Slave執行部分重同步操作;否則,需要執行完整重同步操作。

3)服務器ID(run ID)

  • 每個Redis服務器,Master、Slave、都有run ID自動生成。

  • 當Slave對Master進行初次複製時,Master會將自己的run ID傳送給Slave,Slave會記錄Master的run ID。

  • 當Slave重新連接上一個Master時,會發送之前保存的run ID,用於確認是否爲同一個Master。

1. PSYNC—fullresync

過程如上圖所示,拋去剛開始的建立連接不談,對於初次複製,它會發送一個“PSYNC ? -1”命令,主動請求主服務器去進行完整的重同步。

主服務器接收到命令之後會返回FULLRESYNC,表示執行完整重同步,下面的步驟就和SYNC是一樣的。

2. PSYNC—partial resync

當在增量階段突然斷線,它會再次建立連接。Slave向主服務器發送run ID和offset,run ID就是上一次複製的主服務器的運行ID,offset就是從服務器當前的複製偏移量。接收到命令之後,主服務器會通過這兩個參數去判斷,如果判斷條件滿足,就返回CONTINUE,之後發送緩衝區中的那些寫命令再去執行,就回到了一致的狀態。

二、RedisShake基本原理

(一)主要功能

Redis的主要功能有解析、恢復、備份、同步。

  • 同步SYNC

支持源Redis和目的Redis的數據同步,支持全量和增量數據的遷移。

  • 同步RUMP

支持源Redis和目的Redis的數據同步,僅支持全量的遷移。

  • 備份DUMP

將源Redis的全量數據通過RDB文件備份起來。

  • 恢復Restore

將RDB文件恢復到目的Redis數據庫。

  • 解析Decode

對RDB文件進行讀取,並以json格式解析存儲。

(二)基本同步模式

基本同步模式流程圖

  • 支持的Redis形態:

1)Standalone:單源拉取,主從版/單節點

2)Sentinel:從Sentinel獲取地址並拉取

3)Cluster:開源Cluster模式

4)Proxy:從Proxy拉取

下面舉例說明:

現在要將一個主從版的數據遷移到集羣版,可以在配置文件中配置源端信息和目的端信息,同時在配置地址的時候,對於集羣版可以配置一個分片的地址,因爲它是支持自動發現的。

(三)數據流圖

上圖是SYNC模式下全量+增量的數據流圖,第一次複製向源端發送了之後,源端給它發RDB文件,解析RDB文件放到Pipe中。

它可以通過併發Restore到目的端,同步完成之後就會自動啓動增量。增量就是從源端拉取寫命令,進行解析過濾,然後再放到目的端,同時還需要接收回復。

(四)全量同步

首先可以看全量同步,DbSyncer維護了連接鏈路,它是一個結構體,源端是6379,目的端是一個集羣,它代表了一個數據鏈路。

假如說原來配置的是一個三主三從的集羣模式,那麼就會開啓三個DbSyncer,同時有ID對它進行標識。每個DbSyncer管理一個連接,如6379首先是向RDB文件發送過來,然後Loader會解析出一些信息,包括DB、Key、Type等,然後放到Pipe中。然後需要向目的端寫入,寫入的時候全量模式支持併發。

從Pipe出來後先進行過濾,因爲RedisShake支持DB、Key、Slot這些級別的過濾,用戶可以設置黑白名單。正常情況下就可以Restore到目的端,然後再接收回復。對於lua ,可以進行script load,也可以設置過濾lua。對於BigKey,需要根據編碼形式進行拆分解析。

(五)增量同步

全量同步結束之後會自動開啓增量同步,增量同步主要是由四個協程完成。

第一個協程定期從源端獲取RedisShake的偏移量。

第二個協程主要是從源端拉取寫命令。因爲原來是模擬源端的一個從,從執行寫命令的時候會發送一條Binlog到RedisShake,RedisShake讀到之後交給Decoder,然後會將Binlog解析成一個原生命令,同時還會計算一下偏移量。得到命令後有Key,然後就可以進行過濾,再放到Sendbuffer中,Sendbuffer就是最終要發送到目的端的一些寫命令。

第三個協程就是Sender,它執行發送邏輯,從Sendbuffer中取出寫命令,並不是立即發送過去,它是和Checkpoint綁成一個事務,一塊寫入目的端。斷點續傳的原理是會先放到cache裏邊,就像是一層緩存,然後觸發一定條件之後就去執行sendFunc函數。這是一個事務,它會先去寫這些增數據,然後再去修改Checkpoint值。

第四個協程就是接收目的端的回覆,可能是寫入成功,也可能是寫入出錯的,主要用於統計。

下面我們模擬發送一個命令,觀察會發生什麼事情。

首先源客戶發送了一個“SET msg hello”,然後源端會以一個Binlog的形式寫入到RedisShake。RedisShake讀到之後先解析出原生的寫命令,就會調用Send函數。Send函數目的端是一個集羣,它需要知道自己應該發送到哪個節點上。

因爲集羣方式有槽概念,RedisShake還維護了 Slot數組,它的 index就是槽的ID,它對應的值就是槽所在的節點上。Node是一個結構體,裏邊有一個字段address,就是節點的地址。

調用Send函數的時候,首先需要獲取到這個Key是應該發送到哪個節點上,會調用getNodeByKey,實際上就是對Key進行一次Hash,調用的是crc16算法。Key會算到它在哪個槽上,然後再通過下面 Slots數組,就知道這個槽是在哪個節點上,然後再調用do把它寫入到 node上,寫入之後會Receive目的端的回覆。然後我們就需要去檢查這個回覆是什麼,如果是有異常的話,就會直接將這個結果返回過去,再由上一層去進行判斷。

假如說是move或者是ask,可能會調用回調函數handleMove或handleAsk兩個重定向。

(六)MOVE/ASK重定向

1.MOVE****重定向

上圖爲MOVE重定向流程圖,因爲RedisShake是模擬一個客戶端,然後RedisShake向節點寫命令時,這個節點也會進行一次Hash去看它是不是應該在寫到自身。如果是的話就執行,不是的話就返回一個MOVE。

這個MOVE代表這個Key應該寫到這個節點上,因爲目的端可能會進行擴容,所以說槽的分佈可能是會有變化,RedisShake支持進行擴縮容。收到MOVE的時候,它會提取到目標節點的信息,再向目標節點重新發送鍵命令。

2.ASK****重定向

上方爲ASK重定向流程圖,主要是發生在Slots遷移的時候,RedisShake向A節點發送鍵命令的時候,A在同一向裏回覆一個ASK轉向,表示這一次向B去寫入,然後RedisShake再向B發送鍵命令。

(七)斷點續傳原理

斷點續傳是RedisShake2.0版本增加的功能,是根據Redis PSYNC協議寫的,下方是它的流程圖。

如上圖所示,目的端開啓了斷點續傳,獲取到Checkpoint保存的最大偏移量,然後通過PSYNC指令發送源端,然後在源端去檢查是否合法。

如果需要的寫指令還在複製緩存區中,它就會回覆一個Continue,然後進入增量同步,如果不是的話就會進行全量同步。

主從版的斷點續傳方案會在每個節點DB上記錄一個Checkpoint,名字固定是RedisShake Checkpoint,類型是Hash。

在寫數據的時候,Redis是將Checkpoint和數據捆綁成一個事務寫入目的端的。

  • 討論兩個問題:

1)爲什麼每個邏輯DB都記錄一個Checkpoint?

2)爲什麼要將Checkpoint和數據捆綁成一個事務寫入目的Redis端?

關於問題一,分DB存儲的優勢就是在剛啓動的時候,只需要拉取所有DB的offset,挑取最大的就知道它是在哪個DB上,上一次是在哪個DB,以及同步的最大偏移量。

關於問題二,捆綁封裝成一個事務是爲了保證一致性。

三、應用場景

(一)數據同步——SYNC

在SYNC模式下支持全量同步與增量同步,當然它有一個限制,需要源端支持PSYNC/SYNC,在向源端發送SYNC/PSYNC命令的時候,可以收到源端的回覆。源端、目的端的形態可以是主從集羣/Proxy/Cluster,遷移也可以用於雲下到雲上,雲上到雲下這種混合雲遷移。

(二)多雲廠商之間數據遷移

對於某些雲上redis,比如部分雲廠商不支持SYNC/PSYNC權限,如何進行行遷移?

RedisShake對於這種場景也做了支持,比如繞過SYNC和PSYNC的同步方式。它是以Scan的方式從源端Redis獲取到全量數據,再寫入到目的端,實現數據遷移。

比如有一個fetcher,它會先Scan一定的數據量, 然後配置COUNT,然後將收到的Key給DUMP下來放到PIPE中,然後writer從PIPE中取到Restore到目的端,同時它也有BigKey的優化。

(三)多活

多活通常是用於解決因地域網絡傳輸層面帶來的問題,也就是異地多活。比如說業務層面想要在北京機房寫入一條數據,要求在上海機房也能夠讀到,同樣反過來也可以,這樣會需要做到一個多活。

上圖給出來的是一個根據RedisShake創建多活的方案,是一種僞多活而不是真正的多活。

用戶需要編寫一個Proxy進行流量分發,比如對a或者是b庫的寫操作都分發在左邊的DB,對c庫的寫操作都分發到右邊的DB。RedisShake提供了按庫過濾的功能,可以配置源庫到目的庫的Shake鏈路值,同步ab,然後目的庫到源庫的只同步c,這樣就解決了一個環形複製的問題。

(四)數據備份與恢復

數據備份使用dump功能,就可以直接備份一份rdb,將這些rdb根據時間命名。

  • 數據恢復restore

  • 自建Redis遷移上雲

  • 雲上數據遷移到雲下

  • 將數據庫恢復至之前的狀態,數據回滾

  • 配合Filter使用,只恢復部分Key

  • ……

(五)RDB文件解析decode

  • RDB文件解析 decode

1)讀取RDB文件,以JSON格式解析存儲

2)可以進行Key分析

  • 高版本向低版本遷移

源端版本大於目的端版本,比如源端是4.0,目的端2.8,某些數據結構格式已經修改導致無法同步,可以通過配置繞過這個限制。

  • 掃描遷移 rump

以SCAN的方式從源端Redis獲取全量數據,寫入到目的端,實現數據遷移。

  • 對於不支持sync/psync權限的遷移
  • 跨雲遷移

(六)全球分佈式緩存—全球級聯同步場景

假如用戶業務遍佈全球,有很多個數據中心,之間的數據遷移也可以用RedisShake,它支持全球級聯同步,方便用戶進一步擴大自己的業務。

(七)檢驗遷移後的數據

redis-full-check是用來檢驗兩個Redis數據是否一致的工具。

通常在用RedisShake進行數據遷移之後,可以用這個工具進行校驗,它的原理主要是通過全量對比源端和目的端Redis中的數據方式來進行數據校驗。

如圖可以看到,比較的方式是內部多輪次進行比較,每次比較都會先抓取比較Key。第一輪是從源端進行抓取,後面從Sqlite中進行抓取。抓取之後如果存在有差異的部分,會存在SqliteDB中,後面的抓取直接去更新Sqlite中的數據就可以了。

原文鏈接

本文爲阿里雲原創內容,未經允許不得轉載。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章