1. 程式人生 > >Redis實踐系列丨Codis資料遷移原理與優化

Redis實踐系列丨Codis資料遷移原理與優化

Codis介紹

Codis 是一種Redis叢集的實現方案,與Redis社群的Redis cluster類似,基於slot的分片機制構建一個更大的Redis節點叢集,對於連線到codis的Redis客戶端來說, 除了部分不支援的命令外,與連線開源的 Redis Server 沒有明顯的區別, 客戶端程式碼基本需要進行修改,Codis-proxy會根據訪問的key進行slot的計算,然後轉發請求到對應的Redis-server,對於客戶端來說,中間的codis-proxy是不可見的,因此根據客戶業務的需要,可以使用codis構建大規模的Redis 服務,或者僅僅是用於把請求分擔多個Redis-server提高系統的吞吐量。

 

與業界著名的twproxy相比,除了支援Redis的轉發,coids還支援不停機的資料遷移,使使用者可以在容量或者吞吐量要求有變化時,輕鬆進行節點的增減,本文主要對codis的遷移原理進行分析,並提出一個可行的優化點。

 

本文是基於codis3.0版本。

(圖片來自網路)

 

Codis遷移實現原理

Codis-dashboard在啟動時,運行了4個後臺執行緒(goroutine),包括後臺redis狀態同步、proxy狀態同步、slot事件處理、sync事件處理,並提供了slot相關的RestFUL API進行slot與Redis-group歸屬關係的定義、遷移的定義和觸發。

 

如下結構定義一個slot與Redis-group的歸屬關係和遷移關係,GroupId表示索引為Id的slot所屬的redis-group,而Action用於表示一次遷移,Action.TargetId表示該slot要遷移的目標redis-group的Id,Action.State表示遷移的狀態,主要有Pending、Preparing、Prepared、Migrating、Finished幾種狀態。

type SlotMapping struct {

                Id      int `json:"id"`

                GroupId int `json:"group_id"`

 

                Action struct {

                                Index    int    `json:"index,omitempty"`

                                State    string `json:"state,omitempty"`

                                TargetId int    `json:"target_id,omitempty"`

        UpdatedAt int64 `json:"updated_at,omitempty"`

                } `json:"action"`

}

 

手動進行一次遷移過程,可以用如下命令來觸發:

codis-admin --dashboard=ADDR -slot-action --create --sid=ID --gid=ID,比如把slot 10遷移到group 5,則可以執行” codis-admin --dashboard=ADDR -slot-action --create --sid=10 --gid=5”

如果是把多個slot遷移到同一個server,則可以使用如下命令,一次性來定義若干個遷移操作,codis-admin --slot-action    --create-range --beg=ID --end=ID --gid=ID,比如把slot 10~15遷移到group 5,則可以執行” codis-admin --dashboard=ADDR -slot-action –create--range --beg=10 –end=15 --gid=5”。

 

一次遷移的執行過程中,slot的Action的狀態會發生變化,過程為:

也可以觸發codis進行rebalance,命令為:codis-admin --dashboard=ADDR –rebalance        --confirm,codis會自動把slot往一些新加入的節點進行遷移,使各個節點負責的slot均衡。

 

Codis遷移的測試

經測試,對於一個64G規模的叢集(由8個節點組成,每個節點8G),使用redis-benchmark寫滿資料,每個key的value長度為32位元組,總共寫入341446298(3.4億)條資料,擴容到128G,即對其中的512個slot進行遷移。

 

測試結果如下:

 

從測試結果來看,遷移速度非常慢,每遷移一個slot需要花費基本1個小時,因此使用codis時,需要監控資料量,當資料不夠時,需要進行及時的擴容,否則當空間不夠時的故障處理和恢復時間可能影響線上業務。

 

Codis遷移程式碼分析及瓶頸分析

從測試結果來看,遷移速度確實非常慢,極端情況下可能會影響線上業務,因此對遷移過程進行分析和優化就很有必要,下邊對關鍵的實現程式碼handleSlotRebalance 、StartDaemonRoutines、ProcessSlotAction進行解讀,並分析優化改進的地方。

01

handleSlotRebalance實現分析

這個函式的主要邏輯分為三部分:

1)找到需要遷移的slot;

2)為每個新節點分配slot;

3)生成遷移操作;

 

上面的程式碼的邏輯是:

1)根據節點個數和slot槽數(固定的1024),計算每個節點上應該負責的slot槽數,表示為bound;

2)對每個redis-group,找到需要遷移出去的slot,表示為pending;

 

生成遷移計劃:

1)遍歷所有的redis-group,對於已有的slot小於應該負責的slot槽數的,就要遷移一些槽進來;

2)所有的redis-group,決定需要遷移進來的slot列表,表示為plans;

 

遍歷遷移計劃,使用create actionRange生成一系列的slot action,並儲存到etcd,下一步就需要由後臺執行緒去etcd中取出slot操作進行分別處理。

 

02

StartDaemonRoutines

這個程式碼是在dashboard啟動時就啟動的後臺任務,每隔5秒鐘觸發一次slot操作,且只會執行一個slot操作任務。

 

03

ProcessSlotAction實現分析

分為兩步Topom.SlotActionPrepare和Topom.processSlotAction。

 

從上面程式碼可以看出:

下邊再分析processSlotAction的實現:

 

可以看出:

 

04

瓶頸分析

從上面的分析可以得出:

這個設計的好處是,遷移過程對客戶業務的影響很小,但是也有一些明顯的缺點:

 

由於擴容一般會有一定的提前量,且會選在業務低峰期進行,因此可以對該遷移方案進行優化,可以在不對業務訪問造成太大的影響的前提下提高遷移效率。

 

Codis程式碼優化

根據上面對遷移實現的分析,優化的思路為:

 

1、Slot遷移並行化

從程式碼實現的分析,有2個點可以選擇:

 最終處理程式碼簡單化的考慮,選擇了方案2,同時考慮到如下幾點:

如下優化程式碼,啟動至多10個執行緒進行slot事件的處理。

 

同時修改SlotActionPrepare,選擇一個狀態為Pending且沒有歸屬於同一個redis-server的slot,進行處理。

 

2、Multikey遷移

修改redis-server的遷移指令,支援一次遷移多個key,為了靈活性,把遷移的個數從外部傳入,程式碼比較顯而易見,參考如下:

 

Codis遷移優化測試結果

經過驗證,對於一個64G規模的叢集,使用redis-benchmark寫滿資料,每個key的value長度為32位元組,總共寫入341446298(3.4億)條資料,擴容到128G,即對其中的512個slot進行遷移。最終測試結果為:

因此,經過優化後遷移效能有極大的提升。當然當前的配置也是考慮到了儘量不影響客戶的業務訪問,一次遷移的資料量並不是最大化的,在某些情況下,可以修改配置,一次遷移更多的key,可以更加快速的完成遷移。