Redis 集群的合縱與連橫

之前一篇寫了關(guān)于 Redis 的性能矢赁,這篇就寫寫我認(rèn)為比性能更重要的擴(kuò)展性方面的主題蔓钟。

如果再給我一次回到好幾年前的機(jī)會(huì)蕴轨,對(duì)于使用 Redis 我一開(kāi)始就要好好考慮將來(lái)的擴(kuò)展問(wèn)題词爬。就像我們做數(shù)據(jù)庫(kù)分庫(kù)分表镐躲,一旦決策了分庫(kù)分表,通常一次就會(huì)分到位岖常,比如搞上 8 或 16 個(gè)庫(kù)驯镊,每個(gè)庫(kù)再分 256 或 1024 個(gè)表。不管將來(lái)業(yè)務(wù)再怎么發(fā)展腥椒,基本這個(gè)量級(jí)的分片都足夠應(yīng)對(duì)阿宅,而且底層庫(kù)可以做成邏輯的,扛不住時(shí)再換成物理的笼蛛,對(duì)應(yīng)用方完全透明洒放,沒(méi)有數(shù)據(jù)遷移的煩惱。

而 Redis 其實(shí)也提供了類似的邏輯庫(kù)概念滨砍,每個(gè) Redis 實(shí)例都有 0 到 15 號(hào)獨(dú)立的邏輯庫(kù)空間往湿。當(dāng)我們?cè)缙跈C(jī)器資源緊張而業(yè)務(wù)量又不大時(shí),可以好好根據(jù)業(yè)務(wù)把不同的數(shù)據(jù)放在的單一實(shí)例的不同編號(hào)邏輯庫(kù)上惋戏。這是一種垂直切分方式领追,也可以用水平方式,把 0 到 15 號(hào)邏輯庫(kù)當(dāng)成 16 個(gè)分片來(lái)用响逢,只是這種用法可能對(duì) Client 庫(kù)有些要求绒窑。

總之好幾年前我們都沒(méi)有這樣,當(dāng)時(shí)物理機(jī)資源緊張舔亭,為了考慮不遠(yuǎn)將來(lái)的業(yè)務(wù)擴(kuò)張些膨,所以在有限的資源下決定盡可能的分片。但也沒(méi)分太多钦铺,大約 10 片吧订雾,多了運(yùn)維成本也高。感覺(jué)按 Redis 的性能這一組分片最大承載幾十萬(wàn)每秒的 OPS 估計(jì)能支撐很長(zhǎng)時(shí)間的發(fā)展了矛洞。那 10 片怎么部署呢洼哎?由于每個(gè) Reids 實(shí)例只能利用一個(gè)核,當(dāng)時(shí)的服務(wù)器大概是 16 核沼本,全放一臺(tái)機(jī)也可以噩峦。當(dāng)時(shí)我們正好有 10 臺(tái)物理機(jī),所以很自然的每臺(tái)放了一個(gè)實(shí)例抽兆,但 Redis 只能用一個(gè)核壕探,太浪費(fèi)了。所以每臺(tái)物理機(jī)上除了部署 Reids 還要部署應(yīng)用服務(wù)郊丛,后來(lái)領(lǐng)悟到這又是一個(gè)錯(cuò)誤的部署方式(背景音樂(lè):多么痛的領(lǐng)悟)李请。

一臺(tái) PC Server 的硬件可靠性大約是 99.9%,Redis 作為一個(gè)應(yīng)用全局共享的關(guān)鍵服務(wù)分成 10 片放在十臺(tái) PC Server 上厉熟。實(shí)際上導(dǎo)致整體系統(tǒng)可靠性還降低了一個(gè)量級(jí)导盅,變成了兩個(gè) 9。因?yàn)槿魏我慌_(tái) PC Server 掛了都可能導(dǎo)致全局系統(tǒng)故障揍瑟。然而當(dāng)初沒(méi)有多余的機(jī)器資源白翻,為了提高可靠性,必須對(duì) Redis 做主備绢片,唯一的辦法就是交叉主備滤馍,所以部署結(jié)構(gòu)大概類似下面這樣。

后來(lái)底循,隨著業(yè)務(wù)發(fā)展流量變得越來(lái)越大巢株,Redis 內(nèi)存占用越來(lái)越多,而且開(kāi)始出現(xiàn)一些詭異的故障現(xiàn)象熙涤。比如出現(xiàn)瞬時(shí) Redis 大量連接和處理超時(shí)阁苞,應(yīng)用業(yè)務(wù)線程被阻塞,導(dǎo)致服務(wù)拒絕祠挫,過(guò)一段時(shí)間可能又自動(dòng)恢復(fù)了那槽。這種瞬時(shí)故障非常難抓現(xiàn)場(chǎng),一天來(lái)上幾發(fā)就會(huì)給人業(yè)務(wù)不穩(wěn)定的感受等舔,而一般基礎(chǔ)機(jī)器指標(biāo)的監(jiān)控周期在分鐘級(jí)骚灸。瞬時(shí)故障可能發(fā)生在監(jiān)控的采集間隙,所以只好上腳本在秒級(jí)監(jiān)控日志慌植,發(fā)現(xiàn)瞬時(shí)出現(xiàn)大量 Redis 超時(shí)錯(cuò)誤甚牲,就收集當(dāng)時(shí)應(yīng)用的 JVM 堆棧、內(nèi)存和機(jī)器 CPU Load 等各項(xiàng)指標(biāo)涤浇。終于發(fā)現(xiàn)瞬時(shí)故障時(shí)刻 Redis 機(jī)器 CPU Load 出現(xiàn)瞬間飆升幾百的現(xiàn)象鳖藕,應(yīng)用和 Redis 混合部署時(shí)應(yīng)用可能瞬間搶占了全部 CPU 導(dǎo)致 Redis 沒(méi)有 CPU 資源可用。而應(yīng)用處理業(yè)務(wù)的邏輯又可能需要訪問(wèn) Redis只锭,而 Redis 又沒(méi)有 CPU 資源可用導(dǎo)致超時(shí)著恩,這不就像一個(gè)死鎖么。搞清楚了原因其實(shí)解決方法也簡(jiǎn)單蜻展,就是分離應(yīng)用和 Redis 的部署喉誊,各自資源隔離,自此我們的 Redis 集群發(fā)展開(kāi)始走上一條合縱與連橫的道路纵顾。

合縱

分離應(yīng)用和 Redis 的部署后伍茄,關(guān)于物理機(jī)資源的有一個(gè)尷尬點(diǎn)就是 Redis 單線程機(jī)制帶來(lái)的。當(dāng)時(shí)一臺(tái) PC Server 16 核施逾,內(nèi)存 16 G敷矫,你想多利用核就要多部署實(shí)例例获,但每個(gè)實(shí)例分到的內(nèi)存又不多。最終我們一臺(tái)物理機(jī)只部署 2 個(gè)實(shí)例曹仗,因?yàn)闃I(yè)務(wù)發(fā)展對(duì)內(nèi)存的需求強(qiáng)過(guò)對(duì) CPU 的利用榨汤,所以調(diào)整后的部署模型變成下面這樣。

這樣每個(gè) Redis 實(shí)例能分到的內(nèi)存是小于 8G 的(還要給系統(tǒng)留一點(diǎn)不是)怎茫。隨著業(yè)務(wù)發(fā)展收壕,一開(kāi)始是 2G,很快 4G 然后 6G 就到了單機(jī)內(nèi)存瓶頸轨蛤,下一步只能分一個(gè)實(shí)例出去蜜宪,每個(gè)實(shí)例獨(dú)享單機(jī)內(nèi)存∠樯剑縱向擴(kuò)容在操作性上是最簡(jiǎn)單的圃验,在另外一臺(tái)機(jī)器上先掛一個(gè)從分片,同步復(fù)制完成后枪蘑,通知 Client 端切換連接而分片 Hash 規(guī)則還是不變损谦。這個(gè)過(guò)程會(huì)有短暫的(下圖 2-5 步這個(gè)過(guò)程程序執(zhí)行的時(shí)間窗口)寫丟失,在業(yè)務(wù)上是可接受的岳颇。

獨(dú)享了更大內(nèi)存照捡,我們就可以繼續(xù)縱向擴(kuò)內(nèi)存,但擴(kuò)到了 12G 后就基本到頂了话侧,即便還有更大內(nèi)存的物理機(jī)也不宜再擴(kuò)大單分片的內(nèi)存了栗精。主要原因是因?yàn)?Redis 的主從復(fù)制導(dǎo)致的服務(wù)中斷,當(dāng)初 Redis 版本是 2.4瞻鹏,直到 2.8 才有增量的主從復(fù)制悲立。即使 2.8 主從復(fù)制依然可能在斷鏈長(zhǎng)時(shí)間后導(dǎo)致全量復(fù)制,雖然官方文檔號(hào)稱主從復(fù)制不中斷服務(wù)新博,但實(shí)際每次全量復(fù)制 dump 內(nèi)存時(shí)是阻斷了主線程執(zhí)行薪夕。這個(gè)阻斷時(shí)間在 12G 內(nèi)存時(shí)大概有一分多鐘, 繼續(xù)縱向擴(kuò)大內(nèi)存會(huì)導(dǎo)致更長(zhǎng)時(shí)間的阻斷赫悄,在業(yè)務(wù)上不可接受原献,合縱之路也走到頭了。

連橫

為了對(duì)業(yè)務(wù)做到無(wú)縫透明的擴(kuò)容埂淮,只能走橫向發(fā)展的道路姑隅。而 Redis 官方的 Cluster 方案一直跳票,遲遲出不來(lái)倔撞,大家的業(yè)務(wù)都在快速發(fā)展讲仰,等不及啊。所以在橫向擴(kuò)展上演變出了兩種方案痪蝇,一種是代理模式鄙陡,利用引入中間 Proxy 來(lái)向應(yīng)用層屏蔽后端的集群分布冕房。業(yè)界最早是 Twitter 開(kāi)源的 Twemproxy 采用了這種模式,后來(lái)豌豆莢開(kāi)源的 Codis 進(jìn)一步在運(yùn)維可操作性上完善了這種模式趁矾。主要是在擴(kuò)容方面盡可能的做到業(yè)務(wù)無(wú)感知毒费,思路就是前端引入 Proxy 隔離應(yīng)用層,后端改造 Redis 引入 Slot(有些也叫 Buket)來(lái)分組 key愈魏。應(yīng)用層訪問(wèn)時(shí)基于算法將 key 先映射到 Slot 再映射到具體分片實(shí)例,大概如下面這樣想际。

F(key) -> Slot -> Instance

Redis 中的 key 按 Slot 來(lái)組織培漏,平滑擴(kuò)容時(shí)比如加分片后,按 Slot 為單位遷移胡本,這通常需要改造 Redis 源碼來(lái)支持牌柄。這個(gè)模式的架構(gòu)示意圖如下所示。

引入 Proxy 是犧牲了少量性能來(lái)?yè)Q取了對(duì)應(yīng)用的透明和更好的擴(kuò)展性侧甫。另一種方案是基于 Smart Client 免代理珊佣,但對(duì)應(yīng)用有一定的侵入性,本質(zhì)上就是把 Proxy 的功能放到了 Client披粟。

至于采用哪種方案就是仁者見(jiàn)仁咒锻、智者見(jiàn)智了,需要根據(jù)實(shí)際情況去考慮守屉。不過(guò)個(gè)人認(rèn)為基于代理的方案更靈活些惑艇,而且可在 Proxy 層能做的事情比 Client 要多,但對(duì) Proxy 的實(shí)現(xiàn)要求也更高拇泛。不管上面哪種方案都采用了中心化的控制方式滨巴,中心化對(duì)簡(jiǎn)化運(yùn)維操作是有好處的,而且能方便做到集群全局的管理俺叭。

Redis Cluster 終于遲遲推出后恭取,采用了與中心化不同的思路,而且設(shè)計(jì)目標(biāo)更追求性能熄守,所以是依賴 Smart Client 的方式蜈垮。而 Redis Server 的實(shí)現(xiàn)還是使用了 Slot 方式默認(rèn)最大 16 * 1024 = 16384 個(gè) Slot,所以理論集群最大就是這么多個(gè)實(shí)例柠横,實(shí)際不大可能需要這么大窃款。采用 Gossip 消息來(lái)同步集群配置,基于投票機(jī)制來(lái)進(jìn)行主從 Failover 發(fā)現(xiàn)和自動(dòng)切換牍氛。從目前已經(jīng)推出的版本和功能來(lái)看晨继,作者是在往一個(gè)純 Smart Cluster 方向發(fā)展,但顯然目前的版本還不成熟搬俊。比如自動(dòng)發(fā)現(xiàn)紊扬、集群智能再平衡等功能都沒(méi)有蜒茄,還依賴人工操作。而且非中心化的集群相比中心化集群的可預(yù)測(cè)性和操作性都要差不少餐屎,其在真實(shí)的應(yīng)用案例檀葛,除了在網(wǎng)易有道有人分享了一個(gè)非關(guān)鍵類場(chǎng)景,還沒(méi)見(jiàn)過(guò)比較有份量的成熟案例腹缩。所以 Redis Cluster 的道路恐怕還在路漫漫其修遠(yuǎn)兮屿聋,吾將上下而求索的階段。

總結(jié)

前面回顧了 Redis 集群發(fā)展的歷程藏鹊,從合縱到連橫實(shí)際是一個(gè)從業(yè)務(wù)垂直切分到平臺(tái)服務(wù)化的過(guò)程润讥。至于到底應(yīng)該采用什么樣的集群模式,可能需要好好結(jié)合自身的業(yè)務(wù)發(fā)展階段盘寡、團(tuán)隊(duì)能力和企業(yè)環(huán)境去分析取舍了楚殿。沒(méi)有最好的,只有合適的竿痰。

參考

[1] antirez. Redis Cluster Specification
[2] 黃東旭. 分布式 Redis 架構(gòu)設(shè)計(jì)和踩過(guò)的那些坑們
[3] 西代零零發(fā). 全面剖析 Redis Cluster 原理和應(yīng)用
[4] 西代零零發(fā). Redis Cluster 架構(gòu)優(yōu)化
[5] 楊肉. Redis Cluster 使用經(jīng)驗(yàn)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末脆粥,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子影涉,更是在濱河造成了極大的恐慌变隔,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,640評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件常潮,死亡現(xiàn)場(chǎng)離奇詭異弟胀,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)喊式,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,254評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門孵户,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人岔留,你說(shuō)我怎么就攤上這事夏哭。” “怎么了献联?”我有些...
    開(kāi)封第一講書人閱讀 165,011評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵竖配,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我里逆,道長(zhǎng)进胯,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書人閱讀 58,755評(píng)論 1 294
  • 正文 為了忘掉前任原押,我火速辦了婚禮胁镐,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己盯漂,他們只是感情好颇玷,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,774評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著就缆,像睡著了一般帖渠。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上竭宰,一...
    開(kāi)封第一講書人閱讀 51,610評(píng)論 1 305
  • 那天空郊,我揣著相機(jī)與錄音,去河邊找鬼切揭。 笑死渣淳,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的伴箩。 我是一名探鬼主播,決...
    沈念sama閱讀 40,352評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼鄙漏,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼嗤谚!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起怔蚌,我...
    開(kāi)封第一講書人閱讀 39,257評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤巩步,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后桦踊,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體椅野,經(jīng)...
    沈念sama閱讀 45,717評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,894評(píng)論 3 336
  • 正文 我和宋清朗相戀三年籍胯,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了竟闪。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,021評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡杖狼,死狀恐怖炼蛤,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情蝶涩,我是刑警寧澤理朋,帶...
    沈念sama閱讀 35,735評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站绿聘,受9級(jí)特大地震影響嗽上,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜熄攘,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,354評(píng)論 3 330
  • 文/蒙蒙 一兽愤、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦烹看、人聲如沸国拇。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 31,936評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)酱吝。三九已至,卻和暖如春土思,著一層夾襖步出監(jiān)牢的瞬間务热,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 33,054評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工己儒, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留崎岂,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,224評(píng)論 3 371
  • 正文 我出身青樓闪湾,卻偏偏與公主長(zhǎng)得像冲甘,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子途样,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,974評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容