NO IMAGE

由於Redis出眾的效能,其在眾多的移動網際網路企業中得到廣泛的應用。Redis在3.0版本前只支援單例項模式,雖然現在的伺服器記憶體可以達到100GB、200GB的規模,但是單例項模式限制了Redis沒法滿足業務的需求(例如新浪微博就曾經用Redis儲存了超過1TB的資料)。Redis的開發者Antirez早在部落格上就提出在Redis 3.0版本中加入叢集的功能,但3.0版本等到2015年才釋出正式版。各大企業在3.0版本還沒釋出前為了解決Redis的儲存瓶頸,紛紛推出了各自的Redis叢集方案。這些方案的核心思想是把資料分片(sharding)儲存在多個Redis例項中,每一片就是一個Redis例項。下面介紹Redis的叢集方案。 1.客戶端分片 客戶端分片是把分片的邏輯放在Redis客戶端實現,通過Redis客戶端預先定義好的路由規則,把對Key的訪問轉發到不同的Redis例項中,最後把返回結果彙集。這種方案的模式如圖1所示。

圖片描述

圖1 客戶端分片的模式

客戶端分片的好處是所有的邏輯都是可控的,不依賴於第三方分散式中介軟體。開發人員清楚怎麼實現分片、路由的規則,不用擔心踩坑。 客戶端分片方案有下面這些缺點。 • 這是一種靜態的分片方案,需要增加或者減少Redis例項的數量,需要手工調整分片的程式。 • 可運維性差,叢集的資料出了任何問題都需要運維人員和開發人員一起合作,減緩了解決問題的速度,增加了跨部門溝通的成本。 • 在不同的客戶端程式中,維護相同的分片邏輯成本巨大。例如,系統中有兩套業務系統共用一套Redis叢集,一套業務系統用Java實現,另一套業務系統用PHP實現。為了保證分片邏輯的一致性,在Java客戶端中實現的分片邏輯也需要在PHP客戶端實現一次。相同的邏輯在不同的系統中分別實現,這種設計本來就非常糟糕,而且需要耗費巨大的開發成本保證兩套業務系統分片邏輯的一致性。 2.Twemproxy Twemproxy是由Twitter開源的Redis代理,其基本原理是:Redis客戶端把請求傳送到Twemproxy,Twemproxy根據路由規則傳送到正確的Redis例項,最後Twemproxy把結果彙集返回給客戶端。 Twemproxy通過引入一個代理層,將多個Redis例項進行統一管理,使Redis客戶端只需要在Twemproxy上進行操作,而不需要關心後面有多少個Redis例項,從而實現了Redis叢集。 Twemproxy叢集架構如圖2所示。

圖片描述

圖2 Twemproxy叢集架構

Twemproxy的優點如下。 • 客戶端像連線Redis例項一樣連線Twemproxy,不需要改任何的程式碼邏輯。 • 支援無效Redis例項的自動刪除。 • Twemproxy與Redis例項保持連線,減少了客戶端與Redis例項的連線數。 Twemproxy有如下不足。 • 由於Redis客戶端的每個請求都經過Twemproxy代理才能到達Redis伺服器,這個過程中會產生效能損失。 • 沒有友好的監控管理後臺介面,不利於運維監控。 • 最大的問題是Twemproxy無法平滑地增加Redis例項。對於運維人員來說,當因為業務需要增加Redis例項時工作量非常大。 Twemproxy作為最被廣泛使用、最久經考驗、穩定性最高的Redis代理,在業界被廣泛使用。 3.Codis Twemproxy不能平滑增加Redis例項的問題帶來了很大的不便,於是豌豆莢自主研發了Codis,一個支援平滑增加Redis例項的Redis代理軟體,其基於Go和C語言開發,並於2014年11月在GitHub上開源。 Codis包含下面4個部分。 • Codis Proxy:Redis客戶端連線到Redis例項的代理,實現了Redis的協議,Redis客戶端連線到Codis Proxy進行各種操作。Codis Proxy是無狀態的,可以用Keepalived等負載均衡軟體部署多個Codis Proxy實現高可用。 • CodisRedis:Codis專案維護的Redis分支,新增了slot和原子的資料遷移命令。Codis上層的 Codis Proxy和Codisconfig只有與這個版本的Redis通訊才能正常執行。 • Codisconfig:Codis管理工具。可以執行新增刪除CodisRedis節點、新增刪除Codis Proxy、資料遷移等操作。另外,Codisconfig自帶了HTTP server,裡面整合了一個管理介面,方便運維人員觀察Codis叢集的狀態和進行相關的操作,極大提高了運維的方便性,彌補了Twemproxy的缺點。 • ZooKeeper:分散式的、開源的應用程式協調服務,是Hadoop和Hbase的重要元件,其為分散式應用提供一致性服務,提供的功能包括:配置維護、名字服務、分散式同步、組服務等。Codis依賴於ZooKeeper儲存資料路由表的資訊和Codis Proxy節點的元資訊。另外,Codisconfig發起的命令都會通過ZooKeeper同步到Codis Proxy的節點。 Codis的架構如圖3所示。

圖片描述

圖3 Codis的架構圖

在圖3的Codis的架構圖中,Codis引入了Redis Server Group,其通過指定一個主CodisRedis和一個或多個從CodisRedis,實現了Redis叢集的高可用。當一個主CodisRedis掛掉時,Codis不會自動把一個從CodisRedis提升為主CodisRedis,這涉及資料的一致性問題(Redis本身的資料同步是採用主從非同步複製,當資料在主CodisRedis寫入成功時,從CodisRedis是否已讀入這個資料是沒法保證的),需要管理員在管理介面上手動把從CodisRedis提升為主CodisRedis。 如果覺得麻煩,豌豆莢也提供了一個工具Codis-ha,這個工具會在檢測到主CodisRedis掛掉的時候將其下線並提升一個從CodisRedis為主CodisRedis。 Codis中採用預分片的形式,啟動的時候就建立了1024個slot,1個slot相當於1個箱子,每個箱子有固定的編號,範圍是1~1024。slot這個箱子用作存放Key,至於Key存放到哪個箱子,可以通過演算法“crc32(key)24”獲得一個數字,這個數字的範圍一定是1~1024之間,Key就放到這個數字對應的slot。例如,如果某個Key通過演算法“crc32(key)24”得到的數字是5,就放到編碼為5的slot(箱子)。1個slot只能放1個Redis Server Group,不能把1個slot放到多個Redis Server Group中。1個Redis Server Group最少可以存放1個slot,最大可以存放1024個slot。因此,Codis中最多可以指定1024個Redis Server Group。 Codis最大的優勢在於支援平滑增加(減少)Redis Server Group(Redis例項),能安全、透明地遷移資料,這也是Codis 有別於Twemproxy等靜態分散式 Redis 解決方案的地方。Codis增加了Redis Server Group後,就牽涉到slot的遷移問題。例如,系統有兩個Redis Server Group,Redis Server Group和slot的對應關係如下。 Redis Server Group slot 1 1~500 2 501~1024 當增加了一個Redis Server Group,slot就要重新分配了。Codis分配slot有兩種方法。 第一種:通過Codis管理工具Codisconfig手動重新分配,指定每個Redis Server Group所對應的slot的範圍,例如可以指定Redis Server Group和slot的新的對應關係如下。 Redis Server Group slot 1 1~500 2 501~700 3 701~1024 第二種:通過Codis管理工具Codisconfig的rebalance功能,會自動根據每個Redis Server Group的記憶體對slot進行遷移,以實現資料的均衡。 4.Redis 3.0叢集 Redis 3.0叢集採用了P2P的模式,完全去中心化。Redis把所有的Key分成了16384個slot,每個Redis例項負責其中一部分slot。叢集中的所有資訊(節點、埠、slot等),都通過節點之間定期的資料交換而更新。 Redis客戶端在任意一個Redis例項發出請求,如果所需資料不在該例項中,通過重定向命令引導客戶端訪問所需的例項。 Redis 3.0叢集的工作流程如圖4所示。

圖片描述

圖4 Redis 3.0叢集的工作流程圖

如圖4所示Redis叢集內的機器定期交換資料,工作流程如下。 (1) Redis客戶端在Redis2例項上訪問某個資料。 (2) 在Redis2內發現這個資料是在Redis3這個例項中,給Redis客戶端傳送一個重定向的命令。 (3) Redis客戶端收到重定向命令後,訪問Redis3例項獲取所需的資料。 Redis 3.0的叢集方案有以下兩個問題。 • 一個Redis例項具備了“資料儲存”和“路由重定向”,完全去中心化的設計。這帶來的好處是部署非常簡單,直接部署Redis就行,不像Codis有那麼多的元件和依賴。但帶來的問題是很難對業務進行無痛的升級,如果哪天Redis叢集出了什麼嚴重的Bug,就只能回滾整個Redis叢集。 • 對協議進行了較大的修改,對應的Redis客戶端也需要升級。升級Redis客戶端後誰能確保沒有Bug?而且對於線上已經大規模執行的業務,升級程式碼中的Redis客戶端也是一個很麻煩的事情。 綜合上面所述的兩個問題,Redis 3.0叢集在業界並沒有被大規模使用。 5.雲伺服器上的叢集服務 國內的雲伺服器提供商阿里雲、UCloud等均推出了基於Redis的雲端儲存服務。這個服務的特性如下。 (1)動態擴容 使用者可以通過控制面板升級所需的Redis儲存空間,擴容的過程中服務部不需要中斷或停止,整個擴容過程對使用者透明、無感知,這點是非常實用的,在前面介紹的方案中,解決Redis平滑擴容是個很煩瑣的任務,現在按幾下滑鼠就能搞定,大大減少了運維的負擔。 (2)資料多備 資料儲存在一主一備兩臺機器中,其中一臺機器宕機了,資料還在另外一臺機器上有備份。 (3)自動容災 主機宕機後系統能自動檢測並切換到備機上,實現服務的高可用。 (4)實惠 很多情況下為了使Redis的效能更高,需要購買一臺專門的伺服器用於Redis的儲存服務,但這樣子CPU、記憶體等資源就浪費了,購買Redis雲端儲存服務就很好地解決了這個問題。 有了Redis雲端儲存服務,能使App後臺開發人員從煩瑣運維中解放出來。App後臺要搭建一個高可用、高效能的Redis服務,需要投入相當的運維成本和精力。如果使用雲端儲存服務,就沒必要投入這些成本和精力,可以讓App後臺開發人員更專注於業務。

圖片描述

本文節選自《App 後臺開發運維和架構實踐》一書 電子工業出版社出版 曾健生 著

圖片描述