聊聊 Undermoon - Redis Cluster Slots 遷移
項目地址:https://github.com/doyoubi/undermoon
目標:
- 簡單
- 快速
遷移過程基于以下 Redis 命令:
- SCAN
- DUMP
- PTTL
- RESTORE
- DEL
SCAN 命令有一個很好的特性,它可以保證在第一個 SCAN 命令之前設(shè)置的所有 key 最終都會返回,但有時會返回多次。我們可以執(zhí)行 3 階段遷移來模擬復(fù)制。
- 等待 Redis 完成所有命令。
- 將所有讀寫操作重定向到目標 Redis。如果 key 不存在,則目標 Redis 將需要在處理命令之前從源 Redis 轉(zhuǎn)儲 key 的數(shù)據(jù)。
- 開始掃描并將數(shù)據(jù)轉(zhuǎn)發(fā)到 peer Redis。
詳細步驟
- migrating proxy(遷移代理) 通過 PreCheck 命令檢查 importing proxy(導(dǎo)入代理) 是否也收到遷移任務(wù)。
- migrating proxy 阻塞所有新添加的命令到 Queue,并等待現(xiàn)有命令完成。
- migrating proxy 向 importing proxy 發(fā)送 TmpSwitch 命令。收到此命令后,importing proxy 開始處理導(dǎo)入slot(槽)范圍內(nèi)的 key。當命令返回時,migrating proxy 釋放 Queue 內(nèi)的所有命令,并將它們重定向到 importing proxy。
- migrating proxy 使用 SCAN、PTTL、DUMP、RESTORE、DEL 將遷移 slot 范圍內(nèi)的所有數(shù)據(jù)轉(zhuǎn)發(fā)到 peer importing Redis。RESTORE 不設(shè)置 REPLACE flag。
- importing proxy 在處理命令時,無論是讀操作還是寫操作,都會先
- 將 EXISTS 和處理后的命令發(fā)送到 local importing Redis,如果 EXISTS 返回 true,則將命令轉(zhuǎn)發(fā)到 local importing Redis。
- 如果 EXISTS 返回 false,則發(fā)送 DUMP 和 PTTL 到遷移的 Redis 獲取數(shù)據(jù),并 RESTORE 數(shù)據(jù)并將命令轉(zhuǎn)發(fā)到 local Redis。然后最后將命令轉(zhuǎn)發(fā)到 local importing Redis。
- 如果該命令不會刪除 key,則獲取 key lock,
- 如果該命令可能刪除 key,則獲取 key lock 并將 UMSYNC 發(fā)送到 migrating proxy,讓 migrating proxy 使用 DUMP、PTTL、RESTORE、DEL 將 key 傳輸?shù)?importing proxy。然后最后將命令轉(zhuǎn)發(fā)到 local importing Redis。
- 當 migrating proxy 完成掃描后,它會向 importing proxy 提出 CommitSwitch。然后importing proxy只需要在 local Redis 中處理命令。
- 通知 coordinator 并等待 UMCTL SETCLUSTER 的最終提交。
為什么會這樣設(shè)計
整個遷移過程基于以下命令 SCAN、PTTL、DUMP、RESTORE、DELETE。僅向?qū)敕?wù)器代理發(fā)送 RESTORE 命令,因此為了獲得更好的性能,應(yīng)在遷移服務(wù)器代理中執(zhí)行此掃描和傳輸。
由于掃描和傳輸在服務(wù)器代理和 Redis 上都占用了大量的 CPU 資源,因此最好在importing proxy上處理其他工作負載。因此,一開始我們將所有插槽(slots)直接轉(zhuǎn)移到importing proxy。
此時,importing proxy 仍然只有一小部分數(shù)據(jù)。當它需要處理新添加的 slots 上的命令時,需要在處理請求之前使用 PTTL、DUMP、RESTORE 從遷移的服務(wù)器代理中拉取數(shù)據(jù)。它還需要發(fā)送 DELETE 來刪除 key。
請注意,對于不會刪除 key 的任何命令,由于它是冪等的,因此對同一 key 多次 RESTORE 仍然是正確的。所以僅僅讓 importing proxy 來拉數(shù)據(jù)不會導(dǎo)致任何不一致。
但是對于那些可能刪除 DEL、EXPIRE、KPOP 等 key 的命令,只讓 importing proxy 拉取數(shù)據(jù)可能會導(dǎo)致以下情況:
- key 被刪除
- 還有另一個 RESTORE 命令可以恢復(fù) key。
因此,在提取數(shù)據(jù)時,需要將其與
- importing proxy中的其他 RESTORE 命令。
- migrating proxy中的 SCAN 和 RESTORE。
因此,我們需要在 importing proxy 中鎖定 key,并且需要 migrating proxy 幫助我們發(fā)送數(shù)據(jù)而不是從importing proxy中拉取數(shù)據(jù),以便對該 key 的操作只能按順序處理。
性能
因此,在遷移過程中,遷移和導(dǎo)入 proxy 的工作量非常平衡。migrating proxy 使用 130% 的 CPU,importing proxy使用 80% 的 CPU。
而且遷移 1G 數(shù)據(jù)只用了不到一分鐘。
在測試中,在遷移的同時進行基準測試,吞吐量從 50k 減少到 28k 并逐漸增加到 40k。這是因為在遷移和importing proxy中,SCAN、DUMP、RESTORE 會在 Redis 中消耗大量吞吐量。但是一旦 key 被遷移到導(dǎo)入服務(wù)器代理,它只需要在請求之前發(fā)送一個額外的 EXISTS 命令。
提交遷移后,吞吐量將翻倍。




































