掃二維碼與項(xiàng)目經(jīng)理溝通
我們?cè)谖⑿派?4小時(shí)期待你的聲音
解答本文疑問(wèn)/技術(shù)咨詢/運(yùn)營(yíng)咨詢/技術(shù)建議/互聯(lián)網(wǎng)交流
一、背景

創(chuàng)新互聯(lián)是一家集網(wǎng)站建設(shè),臨淄企業(yè)網(wǎng)站建設(shè),臨淄品牌網(wǎng)站建設(shè),網(wǎng)站定制,臨淄網(wǎng)站建設(shè)報(bào)價(jià),網(wǎng)絡(luò)營(yíng)銷,網(wǎng)絡(luò)優(yōu)化,臨淄網(wǎng)站推廣為一體的創(chuàng)新建站企業(yè),幫助傳統(tǒng)企業(yè)提升企業(yè)形象加強(qiáng)企業(yè)競(jìng)爭(zhēng)力??沙浞譂M足這一群體相比中小企業(yè)更為豐富、高端、多元的互聯(lián)網(wǎng)需求。同時(shí)我們時(shí)刻保持專業(yè)、時(shí)尚、前沿,時(shí)刻以成就客戶成長(zhǎng)自我,堅(jiān)持不斷學(xué)習(xí)、思考、沉淀、凈化自己,讓我們?yōu)楦嗟钠髽I(yè)打造出實(shí)用型網(wǎng)站。
攜程Redis集群規(guī)模和數(shù)據(jù)規(guī)模在過(guò)去幾年里快速增長(zhǎng),我們通過(guò)容器化解決了Redis集群快速部署的問(wèn)題,并根據(jù)實(shí)際業(yè)務(wù)進(jìn)行的一系列嘗試,比如二次調(diào)度、自動(dòng)化漂移等,在內(nèi)存超分的情況下保證了宿主機(jī)的可靠性。
擴(kuò)縮容方面,我們主要通過(guò)垂直擴(kuò)縮容的方式解決Redis集群容量的問(wèn)題,但隨著集群規(guī)模擴(kuò)大,這種方式逐漸遇到了瓶頸。一方面,單個(gè)Redis實(shí)例過(guò)大,會(huì)帶來(lái)較大的運(yùn)維風(fēng)險(xiǎn)和困難;另一方面,宿主機(jī)容量有上限,不能無(wú)止境的擴(kuò)容。考慮到運(yùn)維便利性和資源利用率的平衡,我們希望單個(gè)Redis實(shí)例的上限為15GB。
但實(shí)際操作中卻很難做到:某些業(yè)務(wù)發(fā)展很快,經(jīng)常性需要給Redis進(jìn)行擴(kuò)容,導(dǎo)致單個(gè)實(shí)例大小遠(yuǎn)超15GB;一些業(yè)務(wù)萎縮,實(shí)際使用量遠(yuǎn)低于初始申請(qǐng)的量,造成資源的浪費(fèi)。
如何有效控制Redis實(shí)例大小呢?接下來(lái)本文將帶著這個(gè)問(wèn)題,逐步講解攜程Redis治理和擴(kuò)縮容方面的演進(jìn)歷程。
二、Redis水平擴(kuò)分拆
在攜程開(kāi)始使用Redis很長(zhǎng)一段時(shí)間里,一直只有垂直擴(kuò)縮容,原因有兩點(diǎn):
第一,一開(kāi)始業(yè)務(wù)規(guī)模比較小,垂直擴(kuò)縮容可以滿足需求。垂直擴(kuò)縮容對(duì)于Redis來(lái)說(shuō)只是Maxmemory的配置更改,對(duì)業(yè)務(wù)透明。
第二,水平拆分/擴(kuò)縮容的實(shí)現(xiàn)難度和成本較高。
之前文章《攜程Redis治理演進(jìn)之路》中已經(jīng)提到,攜程訪問(wèn)所有的Redis集群使用的是自主研發(fā)的CRedis,而部署在應(yīng)用端的CRedis通過(guò)一致性hash來(lái)訪問(wèn)實(shí)際承載數(shù)據(jù)的Redis實(shí)例。但一致性hash是無(wú)法支持直接水平擴(kuò)縮容的。因?yàn)闊o(wú)論增加一個(gè)節(jié)點(diǎn)或者刪除一個(gè)節(jié)點(diǎn),都會(huì)導(dǎo)致整個(gè)hash環(huán)的調(diào)整。
圖1
如圖所示,假設(shè)原始有4個(gè)分片(圖1)。當(dāng)添加一個(gè)節(jié)點(diǎn)后,它會(huì)導(dǎo)致某一部分的key本來(lái)是寫(xiě)到nodeC上而現(xiàn)在會(huì)被寫(xiě)到nodeE上,也就是無(wú)法命中之前的節(jié)點(diǎn)。從客戶端的角度來(lái)看,key就像是丟失了。而變動(dòng)的節(jié)點(diǎn)越多,key丟失的也越多,假設(shè)某個(gè)集群從10分片直接添加到20分片,它直接會(huì)導(dǎo)致50%的key丟失。刪除一個(gè)節(jié)點(diǎn)同理,就不再贅述。
因此盡管一致性hash是個(gè)比較簡(jiǎn)單優(yōu)秀的集群方案,但無(wú)法直接水平擴(kuò)容一直困擾著運(yùn)維和架構(gòu)團(tuán)隊(duì)。為此,CRedis團(tuán)隊(duì)在2019年提出了水平拆分的方案。
CRedis水平分拆的思路比較樸素,因?yàn)樵谝恢滦詇ash同一個(gè)水平位置增加節(jié)點(diǎn)會(huì)導(dǎo)致數(shù)據(jù)丟失,那么不改變?cè)瓉?lái)層次節(jié)點(diǎn)的hash規(guī)則,以某個(gè)節(jié)點(diǎn)為hash的起點(diǎn),再來(lái)進(jìn)行一次一致性hash,演變成樹(shù)的結(jié)構(gòu)(圖2)。
圖2
如上圖所示,將樹(shù)形結(jié)構(gòu)從一層拓展成二層,如果繼續(xù)拆分新的葉子Group,則可以將樹(shù)形結(jié)構(gòu)拓展到三層,拆分方案可以支持到十層。葉子 Group是物理分片,直接對(duì)應(yīng)的 Redis 實(shí)例,分支 Group 是虛擬分片,當(dāng)Hash 命中到分支 Group 后,并沒(méi)有找不到對(duì)應(yīng)的Redis實(shí)例,需要再繼續(xù)向下尋找,直到找到葉子 Group 為止。
圖3
CRedis水平分拆上線后,DBA將現(xiàn)存的絕大部分超過(guò)15G的實(shí)例都拆分成更小的實(shí)例,在一段時(shí)間內(nèi)緩解了大內(nèi)存實(shí)例的運(yùn)維治理壓力。但隨著Redis規(guī)模的快速增長(zhǎng),不斷有大的實(shí)例集群出現(xiàn),此外CRedis水平分拆的缺點(diǎn)也逐漸暴露出來(lái):
由此可見(jiàn),水平分拆的方案雖然解決了實(shí)例過(guò)大的問(wèn)題,但不能縮容的弊端也逐漸凸現(xiàn)了出來(lái)。尤其是在今年因疫情影響需要降本增效的背景下,一方面資源比充足,一方面宿主機(jī)上跑的都是無(wú)法縮容的實(shí)例。那么是否有更好的解決方案呢?答案是有的。
三、Redis水平擴(kuò)縮容
1、設(shè)計(jì)思路
圖4
既然縮分片比較困難,我們首先想到的是業(yè)務(wù)雙寫(xiě)集群的方法,也就是業(yè)務(wù)同時(shí)雙寫(xiě)2個(gè)新老集群,新老集群的分片數(shù)是不一樣的,并且大小配置也不一樣。比如之前申請(qǐng)4個(gè)分片現(xiàn)在發(fā)現(xiàn)資源過(guò)剩,讓業(yè)務(wù)創(chuàng)新申請(qǐng)一個(gè)新的2個(gè)分片的集群,由業(yè)務(wù)來(lái)控制灰度寫(xiě)哪個(gè)集群(圖4)。最終會(huì)遷移到新集群上,而新集群大小是滿足當(dāng)前業(yè)務(wù)需求的,從而達(dá)到了縮容的目的。
雙寫(xiě)集群的方案雖然解決我們部分的問(wèn)題,但對(duì)于業(yè)務(wù)的侵入比較深,此外由于雙寫(xiě)集群引入了業(yè)務(wù)配合觀察的時(shí)間,整體流程也比較長(zhǎng)。所以,我們需要尋找更好的解決方案。
既然業(yè)務(wù)雙寫(xiě)集群可以達(dá)到要求,基礎(chǔ)設(shè)施如果代替業(yè)務(wù)做完這部分豈不是更好?借鑒業(yè)務(wù)雙寫(xiě)集群的思路和云原生的不可變基礎(chǔ)設(shè)施的理念,我們首先想到的是通過(guò)新集群替換老集群而不是原地修改集群;另外,為了在公有云上節(jié)省Redis成本,我們積累了kvrocks的實(shí)踐經(jīng)驗(yàn),兩者相結(jié)合,設(shè)計(jì)了一種高效的水平擴(kuò)縮容的方案。
本方案的核心是引入了一個(gè)基于kvrocks改造的中間態(tài)binlogserver,它既是一個(gè)老集群的Slave節(jié)點(diǎn),又充當(dāng)了新集群的客戶端。一方面,它會(huì)從Redis Master復(fù)制全量和增量數(shù)據(jù);另一方面,它又充當(dāng)客戶端的角色,將復(fù)制來(lái)的數(shù)據(jù)按照新集群的一致性HASH規(guī)則寫(xiě)往新的集群。大致的步驟如下,具體的步驟流程可以參考下面的圖所示(圖5)。
圖5
通過(guò)Redis的水平擴(kuò)縮容方案,我們解決了之前的幾個(gè)痛點(diǎn)問(wèn)題:
2、運(yùn)維數(shù)據(jù)
水平擴(kuò)縮容方案上線4個(gè)月來(lái),已經(jīng)成功完成了200多次的擴(kuò)容和縮容。今年某個(gè)業(yè)務(wù)突然請(qǐng)求量暴增十幾倍,相關(guān)集群經(jīng)歷了多次擴(kuò)容,每次擴(kuò)容大多在10分鐘內(nèi)完成,有效地支撐了業(yè)務(wù)發(fā)展。
另一方面,針對(duì)申請(qǐng)分片非常多而大但實(shí)際使用量非常小的集群,我們也借助水平擴(kuò)縮容的能力快速地縮小了分片數(shù)和申請(qǐng)量。通過(guò)這些縮容,有效地提升了整體的資源利用率。
3、一些坑
(1)單個(gè)key過(guò)大導(dǎo)致key被驅(qū)逐
在實(shí)際水平擴(kuò)縮容過(guò)程中,我們發(fā)現(xiàn)有些集群,單個(gè)實(shí)例中可能會(huì)有巨大的key(大于3G),由于V2集群的大小是根據(jù)V1大小實(shí)時(shí)算出來(lái)的平均值,一旦V1中某個(gè)實(shí)例過(guò)大,可能會(huì)導(dǎo)致寫(xiě)到V2中的某個(gè)實(shí)例大小大于預(yù)期的平均值,從而引起某些key被驅(qū)逐。因此,針對(duì)這種情況:
(2)mget擴(kuò)容后會(huì)導(dǎo)致性能下降
對(duì)于極個(gè)別的場(chǎng)景,我們還發(fā)現(xiàn),mget請(qǐng)求耗時(shí)會(huì)有明顯上升,主要原因還是在于,擴(kuò)容之前mget需要訪問(wèn)的實(shí)例數(shù)少,而分拆后訪問(wèn)的實(shí)例數(shù)變多。一般這種情況,我們建議業(yè)務(wù)控制單次mget的key的數(shù)量,或者將string類型改造為hash類型,通過(guò)hmget來(lái)訪問(wèn)數(shù)據(jù),保證每次只會(huì)訪問(wèn)到一個(gè)實(shí)例,這樣擴(kuò)容后其吞吐量是隨著分片數(shù)量線性增加,而延遲不會(huì)有增加。
四、總結(jié)和未來(lái)規(guī)劃
1、Xpipe支持
目前水平擴(kuò)縮容和漂移以及二次調(diào)度等一系列治理工具和策略組成了一個(gè)比較完善的閉環(huán),有效地支撐了生產(chǎn)幾千臺(tái)宿主機(jī),幾萬(wàn)帶超分能力Redis實(shí)例的運(yùn)維治理。
但目前受制于xpipe的架構(gòu),對(duì)于接入了xpipe的集群,必須先擴(kuò)縮容后再將DR端的xpipe人工補(bǔ)齊,自動(dòng)化程度還不足,而補(bǔ)齊xpipe的時(shí)間比較長(zhǎng),比如之前是就近讀本機(jī)房的Redis集群的APP,在擴(kuò)縮容后可能一段時(shí)間里只能跨機(jī)房讀取,必然導(dǎo)致延遲上升。而這種延遲上升又會(huì)影響我們對(duì)于水平擴(kuò)縮容邏輯是否正確,是否需要回退的判斷。因此后續(xù)我們會(huì)針對(duì)xpipe集群,也做到和普通集群一樣,也就是V2集群在擴(kuò)縮容寫(xiě)流量之前就是帶DR架構(gòu)的集群。
2、持久化KV存儲(chǔ)的支持
除了Redis本身受業(yè)務(wù)歡迎使用廣泛外,我們還發(fā)現(xiàn)有些業(yè)務(wù)需要相比Redis 更可靠的KV存儲(chǔ)方式,比如數(shù)據(jù)保存在磁盤(pán)上而不是保存在內(nèi)存里,再比如業(yè)務(wù)需要支持一些增減庫(kù)存邏輯,對(duì)某個(gè)key的獨(dú)占訪問(wèn),實(shí)現(xiàn)語(yǔ)義近似INCRBY操作,但實(shí)際上是對(duì)于一些字符串進(jìn)行merge操作。此外數(shù)據(jù)可靠性要求更高,master宕機(jī)不能丟失數(shù)據(jù)等。針對(duì)這些需求目前我們已經(jīng)也有一些實(shí)踐經(jīng)驗(yàn),將在后續(xù)文章中分享。

我們?cè)谖⑿派?4小時(shí)期待你的聲音
解答本文疑問(wèn)/技術(shù)咨詢/運(yùn)營(yíng)咨詢/技術(shù)建議/互聯(lián)網(wǎng)交流