標籤:
Codis——分布式Redis服務的解決方案
之前介紹過的 Twemproxy 是一種Redis代理,但它不支援叢集的動態伸縮,而codis則支援動態增減Redis節點;另外,官方的redis 3.0開始支援cluster。
codis和twemproxy最大的區別有兩個:
- codis支援動態水平擴充,對client完全透明不影響服務的情況下可以完成增減redis執行個體的操作;
- codis是用go語言寫的並支援多線程,twemproxy用C並只用單線程。 後者又意味著:codis在多核機器上的效能會好於twemproxy;codis的最壞回應時間可能會因為GC的STW而變大,不過go1.5發布後會顯著降低STW的時間;如果只用一個CPU的話go語言的效能不如C,因此在一些短串連而非長串連的情境中,整個系統的瓶頸可能變成accept新tcp串連的速度,這時codis的效能可能會差於twemproxy。
codis和redis cluster的區別:
redis cluster基於smart client和無中心的設計,client必須按key的雜湊將請求直接發送到對應的節點。這意味著:使用官方cluster必須要等對應語言的redis driver對cluster支援的開發和不斷成熟;client不能直接像單機一樣使用pipeline來提高效率,想同時執行多個請求來提速必須在client端自行實現非同步邏輯。 而codis因其有中心節點、基於proxy的設計,對client來說可以像對單機redis一樣去操作proxy(除了一些命令不支援),還可以繼續使用pipeline並且如果後台redis有多個的話速度會顯著快於單redis的pipeline。同時codis使用zookeeper來作為輔助,這意味著單純對於redis叢集來說需要額外的機器搭zk,不過對於很多已經在其他服務上用了zk的公司來說這不是問題:)
Codis 是豌豆莢公司開發的一個分布式 Redis 解決方案,用Go語言開發的。對於上層的應用來說,串連到 Codis Proxy 和串連原生的 Redis Server 沒有明顯的區別 (不支援的命令列表),Codis 底層會處理請求的轉寄,不停機的資料移轉等工作。所有後邊的一切事情,對於前面的用戶端來說是透明的,可以簡單的認為後邊串連的是一個記憶體無限大的 Redis 服務。
Codis 由四部分組成:
- Codis Proxy (codis-proxy),處理用戶端請求,支援Redis協議,因此用戶端訪問Codis Proxy跟訪問原生Redis沒有什麼區別;
- Codis Dashboard (codis-config),Codis 的管理工具,支援添加/刪除 Redis 節點、添加/刪除 Proxy 節點,發起資料移轉等操作。codis-config 本身還內建了一個 http server,會啟動一個 dashboard,使用者可以直接在瀏覽器上觀察 Codis 叢集的運行狀態;
- Codis Redis (codis-server),Codis 項目維護的一個 Redis 分支,基於 2.8.21 開發,加入了 slot 的支援和原子的資料移轉指令;
- ZooKeeper/Etcd,Codis 依賴 ZooKeeper 來存放資料路由表和 codis-proxy 節點的元資訊,codis-config 發起的命令都會通過 ZooKeeper 同步到各個存活的 codis-proxy;
Codis 支援按照 Namespace 區分不同的產品,擁有不同的 product name 的產品,各項配置都不會衝突。
Codis 採用 Pre-sharding 的技術來實現資料的分區,預設分成 1024 個 slots (0-1023),對於每個key來說,通過以下公式確定所屬的 Slot Id:
SlotId = crc32(key) % 1024
每一個 slot 都會有一個且必須有一個特定的 server group id 來表示這個 slot 的資料由哪個 server group 來提供。資料的遷移也是以slot為單位的。
安裝與部署
- 安裝go;
- 安裝codis
go get -u -d github.com/CodisLabs/codiscd $GOPATH/src/github.com/CodisLabs/codismake
- 安裝zookeeper;
- 啟動dashboard
bin/codis-config dashboard
- 初始化slots,在zk上建立slot相關資訊
bin/codis-config slot init
- 啟動codis-redis,跟官方redis server方法一樣;
- 添加redis server group,每個 Group 作為一個 Redis 伺服器組存在,只允許有一個 master, 可以有多個 slave,group id 僅支援大於等於1的整數。如: 添加兩個 server group, 每個 group 有兩個 redis 執行個體,group的id分別為1和2, redis執行個體為一主一從。
bin/codis-config server add 1 localhost:6379 masterbin/codis-config server add 1 localhost:6380 slavebin/codis-config server add 2 localhost:6479 masterbin/codis-config server add 2 localhost:6480 slave
- 設定server group 服務的 slot 範圍,如設定編號為[0, 511]的 slot 由 server group 1 提供服務, 編號 [512, 1023] 的 slot 由 server group 2 提供服務
bin/codis-config slot range-set 0 511 1 onlinebin/codis-config slot range-set 512 1023 2 online
- 啟動codis-proxy,
bin/codis-proxy -c config.ini -L ./log/proxy.log --cpu=8 --addr=0.0.0.0:19000 --http-addr=0.0.0.0:11000
剛啟動的 codis-proxy 預設是處於 offline狀態的, 然後設定 proxy 為 online 狀態, 只有處於 online 狀態的 proxy 才會對外提供服務
bin/codis-config -c config.ini proxy online <proxy_name> <---- proxy的id, 如 proxy_1
資料移轉(migrate)
安全和透明的資料移轉是 Codis 提供的一個重要的功能,也是 Codis 區別於 Twemproxy 等靜態分布式 Redis 解決方案的地方。
資料移轉的最小單位是 key,我們在 codis redis 中添加了一些指令,實現基於key的遷移,如 SLOTSMGRT等 (命令列表),每次會將特定 slot 一個隨機的 key 發送給另外一個 codis redis 執行個體,這個命令會確認對方已經接收,同時刪除本地的這個 k-v 索引值,返回這個 slot 的剩餘 key 的數量,整個操作是原子的。
在 codis-config 管理工具中,每次遷移任務的最小單位是 slot。如: 將slot id 為 [0-511] 的slot的資料,遷移到 server group 2上,--delay 參數表示每遷移一個 key 後 sleep 的毫秒數,預設是 0,用於限速。
bin/codis-config slot migrate 0 511 2 --delay=10
遷移的過程對於上層業務來說是安全且透明的,資料不會丟失,上層不會中止服務。
注意,遷移的過程中打斷是可以的,但是如果中斷了一個正在遷移某個slot的任務,下次需要先遷移掉正處於遷移狀態的 slot,否則無法繼續 (即遷移程式會檢查同一時刻只能有一個 slot 處於遷移狀態)。
自動重新平衡(auto rebalance)
Codis 支援動態根據執行個體記憶體,自動對slot進行遷移,以均衡資料分布
bin/codis-config slot rebalance
要求:
- 所有的codis-server都必須設定了maxmemory參數;
- 所有的 slots 都應該處於 online 狀態, 即沒有遷移任務正在執行;
- 所有 server group 都必須有 Master;
高可用(HA)
因為codis的proxy是無狀態的,可以比較容易的搭多個proxy來實現高可用性並橫向擴容。
對Java使用者來說,可以使用經過我們修改過的Jedis,Jodis ,來實現proxy層的HA。它會通過監控zk上的註冊資訊來即時獲得當前可用的proxy列表,既可以保證高可用性,也可以通過輪流請求所有的proxy實現負載平衡。如果需要非同步請求,可以使用我們基於Netty開發的Nedis。
對下層的redis執行個體來說,當一個group的master掛掉的時候,應該讓管理員清楚,並手動的操作,因為這涉及到了資料一致性等問題(redis的主從同步是最終一致性的)。因此codis不會自動的將某個slave升級成master。 不過我們也提供一種解決方案:codis-ha。這是一個通過codis開放的api實現自動切換主從的工具。該工具會在檢測到master掛掉的時候將其下線並選擇其中一個slave提升為master繼續提供服務。
需要注意,codis將其中一個slave升級為master時,該組內其他slave執行個體是不會自動改變狀態的,這些slave仍將試圖從舊的master上同步資料,因而會導致組內新的master和其他slave之間的資料不一致。因為redis的slave of命令切換master時會丟棄slave上的全部資料,從新master完整同步,會消耗master資源。因此建議在知情的情況下手動操作。使用 codis-config server add <group_id> <redis_addr> slave
命令重新整理這些節點的狀態即可。codis-ha不會自動重新整理其他slave的狀態。
Codis——分布式Redis服務的解決方案