天天看點

餓了麼Redis Cluster叢集化演進

2017運維/devops線上技術峰會上,餓了麼運維負責人程炎嶺帶來題為“餓了麼redis cluster叢集化演進”的演講。本文主要從資料和背景開始談起,并對redis的治理進行分析,接着分享了redis cluster的優缺點,重點分析了corvus,包括commands、邏輯架構和實體部署等,最後分享了redis的運維和開發,并作了簡要總結,一起來瞧瞧吧。

以下是精彩内容整理:

近幾個月,運維事件頻發。從“爐石資料被删”到“mongodb遭黑客勒索”,從“gitlab資料庫被誤删”到某家公司漏洞被組合攻擊。這些事件,無一不在呐喊——做好運維工作的重要性。然而,從傳統it部署到雲,人肉運維已經是過去式,雲上運維該怎麼開展?尤其是雲2.0時代,運維已經向全局化、流程化和精細化模式轉變。與此同時,人工智能的發展,“威脅論”也随之襲來——運維是不是快要無用武之地了?如何去做更智能的活,當下很多運維人在不斷思考和探尋答案。

redis cluster在業内有唯品會、優酷藍鲸等很多公司都已經線上上有了一定規模的redis cluster叢集,那麼,餓了麼是怎樣從現有的redis技術遷移到redis cluster叢集上的呢?

背景和資料

2015-2016年是餓了麼的業務爆炸式增長階段,訂單量從幾十w到到峰值900w/天,在增長的過程中我們也遇到了一些問題,我們有服務化治理訴求、應用性能和穩定訴求、人肉運維成本訴求,并且還要為未來的paas平台池化準備。

餓了麼Redis Cluster叢集化演進

橫坐标是訂單量,縱坐标是叢集數量,在多機房下整個叢集規模是double的,可以看到,千萬訂單的叢集規模大概在200個,在一個機房内我們有近200套叢集,近4t資料在redis裡面,整個節點數大約在10000個左右。

餓了麼Redis Cluster叢集化演進

上圖表示在高峰期時每天不同訂單量下,整個redis叢集承載ops的需求。

<b>使用場景</b><b></b>

外賣行業的使用場景要結合業務來看,所有需要高性能、高并發、降級資料庫場景下都用到了redis做緩存,包括使用者端、商戶端和物流配送,搜尋排序、熱賣、畫像等目前也是往redis裡推送,除了緩存外,還有各類cache、計數器、分布式鎖等。

為什麼會考慮向redis cluster演進呢?

因為redis存在很多問題,比如單點的記憶體上限受限制,将redis當成db或cache混用,共用一個redis、大key阻塞整個執行個體,經常需要擴容、擴容很痛苦,使用複雜且基礎環境非标、存在各種配置,監控缺失以及不能統一配置等諸多問題。

治理

由于redis請求量太大,打滿了機器網卡,是以在2016年5月啟動了治理redis的項目。

在自研選型部分,在ops推廣治理時我們與業内成熟技術做了比較,包括redis vs redis cluster,以及twenproxy vs codis vs redis cluster,比較了單節點的redis和redis

cluster,還有業内經過生産考驗的proxy,最後決定自研自己的redis cluster + proxy。

redis cluster 的優缺點

為什麼選擇redis cluster作為我們的底層存儲呢?

redis cluster自帶遷移功能,性能快、高可用,支援線上分片,豐富叢集管理指令等。自研proxy在初期快速在生産上應用時候可以省略掉這部分,讓redis  cluster本身給我們提供這項功能就足夠了。

redis cluster也有一些不足:

client 實作複雜,需要緩存slot mapping關系并及時更新;

client不成熟導緻提高開發難度;

存儲和分布式邏輯耦合,節點太多時節點之間的檢測占大量網卡帶寬;

3.0.6版本前,隻能單個key遷移,并且同時隻允許一個slot處于遷移狀态。

corvus

餓了麼Redis Cluster叢集化演進

圖中為corvus時序設計,可以看到,actor到後面的redis,中間的proxy、client和server是在corvus中實作的邏輯,proxy處理connect操作,接收使用者的請求發送到client,server最終實作指令的處理。我們分成三個分類,對于單key、特殊指令和多key的處理,對複合指令操作做了單獨操作,并對傳回結果做聚合,同時還有一些特殊指令,比如cluster指令的處理邏輯。

corvus封裝了redis cluster 協定,提供redis 協定,這樣用原來成熟的redis

client操作redis cluster叢集,接入到corvus,這時可以做到無縫的改動;擴容縮容應用無感覺,corvus服務是注冊的機制,對應用程式來說,連接配接是通過一層本地proxy,我們對應用程式提供sdk,但目前我們通過本地proxy連接配接接入corvus已經能夠滿足大部分應用;corvus實時緩存了slots mapping,它會不停的發送corvus notes去擷取redis node與slots mapping的關系,并更新到corvus本身的緩存中,還有multiple thread、lightweight和reuseport support等。

<b>corvus commands</b>

corvus支援pipeline;

modified commands,對複合指令做spring操作,我們已經對指令本身進行了修改,但可能對我們看監控資料有疑惑,統計端節點命中率與應用程式命中率可能不一樣;

restricted commands;

unsupported commands,比如對gu的處理,目前還沒有在redis cluster中使用gu的應用,暫時不會支援。

<b>corvus performance</b>

餓了麼Redis Cluster叢集化演進

圖中為corvus的測試報告,這是很極端的壓測情況,set/get可以達到140萬qps資料。

<b>corvus </b><b>邏輯架構</b><b></b>

餓了麼Redis Cluster叢集化演進

可以看到,底層存儲用redis cluster,web console通過後端ruskit操作redis cluster,當我們注冊redis cluster時,appid存儲在storage裡,監控部分我們通過esm agent發送到grafana,corvus本身也會采集server log發送到statsd去收集,corvus agent去部署時首先要初始化corvus agent注冊到host console裡,通過corvus agent去拉起或回收某一套redis cluster,huskar是我們的集中配置管理,對應到我們後端的zk技術。

<b>corvus </b><b>實體部署</b><b></b>

餓了麼Redis Cluster叢集化演進

corvus的實體部署如圖所示,上面是應用程式端,應用程式連接配接到本地的corvus服務端口,corvus服務端口作為本地的sam port,一個corvus執行個體對應到redis cluster的一些master節點,會部署在不同的實體伺服器上,根據不同的和數去建立corvus服務執行個體以及redis節點。

這裡有兩層proxy,首先連接配接本地的sam,也就是go proxy,go proxy這層提供應用服務化、本地接入,corvus是redis cluster proxy這一層,兩層proxy意味着兩層的性能損耗,在這個基礎上,我們也做了針對redis cluster相同的直連的性能損失,對比來看,proxy層性能損耗相比運維成本以及服務化成本而言,是可以接受的。

redis 運維和開發

<b>redis</b><b>運維</b><b></b>

我們将redis移交資料庫團隊運維

統一redis 實體部署架構

标準化redis/corvus

伺服器以及單機容量

設定redis 開發設計規範

設定redis資源申請流程,做好容量評估

完善監控,監控分corvus和redis

cluster兩部分,跟進全鍊路壓測,從最前端打進來,到底層的redis和資料庫

根據壓測結果做隔離關鍵叢集,比如整個外賣後端和商戶的redis,将它們做單獨隔離,redis cluster本身對網卡要求很高,分專用資源和公共資源,專用資源的corvus或redis cluster機器獨棧給到appid,公共資源是混布情況,我們隻需監控網卡流量、cpu等

更新萬兆網卡,新采購的伺服器都是萬兆配置

<b>redis </b><b>開發約定</b><b></b>

redis開發約定如下:

key 格式約定:object-type:id:field。用":"分隔域,用"."作為單詞間的連接配接,對于整個field怎樣分隔,我們有很多種改法;

禁止大k-v,我們基于全鍊路壓測,比如一套叢集有多種類型的pattern的key,其中某一個的key value很長,占用記憶體很大,我們就要告訴開發者像這種使用姿勢是不對的,須被禁止;

隻用作cache, key

ttl設定;

做好容量評估,由于redis

cluster的遷移限制,是以前期我們要做好容量評估,比如整套叢集申請100g,我們需要計算需要的記憶體容量以及所在的應用場景負載;

設定叢集上限,規定redis

cluster叢集數不超過200,單個節點容量4~8g;

接入統一為本地goproxy

<b>redis </b><b>改造</b><b></b>

我們要定義一個節奏,對redis進行改造,具體如下:

1.        

将redis 遷移cluster

2.        

完善system、redis、corvus監控

3.        

對線上redis拆分、擴容

4.        

非标機器騰挪下線

5.        

大key、熱節點、大節點掃描,比如同一個redis cluster叢集某個節點明顯高于其它節點,我們會做節點分析和key的分析等

6.        

網卡bond,多隊列綁定

7.        

修改jedis源碼,加入matrix埋點上報etrace,就可以在etrace看到每個應用trace到整條鍊路到哪一個端口,它的響應時間、請求頻次、命中率等。

經驗總結

對于redis我們可謂經驗豐富,總結如下:

redis 更适合cache,cluster下更不合适做持久化存儲

一緻性保證,不做讀寫分離(盡管corvus支援)

單執行個體不宜過大,節點槽位要均衡

發現問題,複盤,積累經驗

不跨機房部署,性能優先

異地多活通過訂閱消息更新緩存

大部分的故障都是由于hot-key/big-key

關心流量,必要時立刻更新萬兆

<b>遺留問題</b><b></b>

目前redis還不夠自動化,某些操作還是分成多個步驟來實作,大叢集reshard擴容慢;遇到版本更新問題時,可以新起一條叢集;sam-&gt;corvus-&gt;redis鍊路分析,應用程式在etrace上通路端口會很慢,需要從redis和corvus層層自證等;多活跨機房寫緩存同步;我們通過定時任務的發起aof/rdb來保證資料盡快恢複,實際上我們是關閉掉的;冷資料也是不夠完善的,還有叢集自愈,當遇到問題時我們可以快速拉起另外一套叢集将資料更新過去,或者通過資料更新預熱将緩存更新過去。

<b>池化</b><b></b>

我們在根據全鍊路壓測時的一些特性或資料去做叢集隔離,分放公共池和專用池,以及對整個池内資源做buffer的監控;資源申請隻需關注容量以及使用方式,無需關心機器;另外,新申請以及擴容縮容按組(group)操作,運維操作自動化,不需要登入到某個機器上執行某個指令。

<b>開發自助</b><b></b>

我們最終的目标是在緩存部分實作開發自助,具體步驟如下:

開發自助申請redis 資源

填入demo key/value,自動計算容量

狀态,告警推送,對線上實時運作情況的透明化

各類文檔齊全

目前我們還做不到,需要ops團隊幹預開發。

繼續閱讀