Redis 如何分布式,來(lái)看京東金融的設(shè)計(jì)與實(shí)踐

轉(zhuǎn)載:Redis 如何分布式,來(lái)看京東金融的設(shè)計(jì)與實(shí)踐


作者 | 李竟成

編輯 | 雨多田光

標(biāo)簽 | 分布式緩存

前 ? 言

R2M 是京東金融線上大規(guī)模應(yīng)用的分布式緩存系統(tǒng),目前管理的機(jī)器總內(nèi)存容量超過(guò) 60TB,近 600 個(gè) Redis Cluster 集群,9200 多個(gè) Redis 實(shí)例。

其主要功能包括:全 web 可視化運(yùn)維、緩存集群一鍵部署資源池統(tǒng)籌管理、在線擴(kuò)容及快速數(shù)據(jù)遷移、多機(jī)房切換及容災(zāi)、完善的監(jiān)控及告警Redis API 兼容等。

本文將從 R2M 系統(tǒng)架構(gòu)、資源管理、集群擴(kuò)容與遷移、數(shù)據(jù)冷熱交換、多機(jī)房容災(zāi)等多方面進(jìn)行深入剖析,希望讀者能有所收獲。

業(yè)務(wù)使用及運(yùn)維上的優(yōu)點(diǎn)

極簡(jiǎn)化接入

R2M 接入簡(jiǎn)單,以 Java 客戶(hù)端為例,只需在業(yè)務(wù)程序中引入客戶(hù)端 jar 包,配置好 appname 及 ZK 地址,即可使用。

配置自動(dòng)下發(fā)

R2M 客戶(hù)端提供了諸如連接池、讀寫(xiě)超時(shí)時(shí)間、重試次數(shù)等配置,有些情況下,需要對(duì)這些配置進(jìn)行調(diào)優(yōu),比如 618、雙十一大促來(lái)臨時(shí),有些業(yè)務(wù)需要減小客戶(hù)端讀寫(xiě)超時(shí)時(shí)間,避免超時(shí)問(wèn)題引發(fā)的連鎖反應(yīng)。

為了修改配置,業(yè)務(wù)不得不重新上線所有的客戶(hù)端,不僅費(fèi)時(shí)費(fèi)力,還有可能引發(fā)故障。因此,為了避免這個(gè)問(wèn)題,R2M 提供了配置自動(dòng)下發(fā)功能,業(yè)務(wù)客戶(hù)端無(wú)需重新上線,管理端修改配置后,所有客戶(hù)端即時(shí)生效。

多種數(shù)據(jù)類(lèi)型 API 支持

完全兼容 Redis 各數(shù)據(jù)類(lèi)型 API 接口,提供包括哈希、集合、有序集合、列表、發(fā)布 / 訂閱等近百個(gè)接口。

支持?jǐn)?shù)據(jù)冷熱交換

在保證高性能和數(shù)據(jù)一致性的前提下,實(shí)現(xiàn)了基于 LFU 熱度統(tǒng)計(jì)的數(shù)據(jù)動(dòng)態(tài)冷熱交換,使得冷數(shù)據(jù)被交換到 SSD 上,熱數(shù)據(jù)被加載到 Redis 上,取得高性能和低成本的高平衡。

全 web 可視化運(yùn)維

R2M 實(shí)現(xiàn)了包括集群部署、擴(kuò)容、數(shù)據(jù)遷移、監(jiān)控、集群下線、數(shù)據(jù)清理、故障節(jié)點(diǎn)替換及機(jī)器下線等在內(nèi)的所有功能的可視化運(yùn)維,運(yùn)維效率及可用性大大提升。

多機(jī)房一鍵切換

通過(guò)改造 Redis Cluster 支持多機(jī)房災(zāi)備、防止集群腦裂,以及各系統(tǒng)組件針對(duì)多機(jī)房切換選舉的支持,實(shí)現(xiàn)了在 Web 控制臺(tái)的多機(jī)房一鍵切換。

多種方式的集群同步及數(shù)據(jù)遷移工具

R2M 提供了專(zhuān)門(mén)的遷移工具組件,支持從原生 Redis 遷移至 R2M,實(shí)現(xiàn)了基于 Redis RDB 文件傳輸及增量同步的遷移機(jī)制。

同時(shí),還可以指定規(guī)則比如按照前綴匹配或者反向匹配進(jìn)行部分?jǐn)?shù)據(jù)遷移。由于內(nèi)部歷史原因,京東金融部分業(yè)務(wù)以前用的是京東商城的 Jimdb,R2M 同樣也支持了從 Jimdb 集群進(jìn)行遷移。

R2M 遷移工具還支持?jǐn)?shù)據(jù)實(shí)時(shí)同步的功能,包括 R2M 集群間的數(shù)據(jù)同步和 Jimdb 源集群的數(shù)據(jù)同步。

非 Java 語(yǔ)言代理

我們的業(yè)務(wù)開(kāi)發(fā)人員主要用的是 Java,對(duì)于非 Java 語(yǔ)言的客戶(hù)端,比如 Python、C 等等,則通過(guò) R2M Proxy 組件接入,各種運(yùn)維操作在 Proxy 層進(jìn)行,對(duì)業(yè)務(wù)方屏蔽。同時(shí),Proxy 也提供了高可用保障方案。

系統(tǒng)架構(gòu)

組件功能

Web console

是 R2M 緩存系統(tǒng)的可視化運(yùn)維控制臺(tái)。所有運(yùn)維操作均在 Web console 進(jìn)行。

Manager

整個(gè)系統(tǒng)的管理組件。負(fù)責(zé)所有運(yùn)維操作的下發(fā)、監(jiān)控?cái)?shù)據(jù)的收集等。運(yùn)維操作包括集群創(chuàng)建、數(shù)據(jù)遷移、擴(kuò)容、多機(jī)房切換等等。

Agent

每臺(tái)物理機(jī)上部署一個(gè) Agent 組件,Agent 負(fù)責(zé)本機(jī) Redis 實(shí)例的部署和監(jiān)控,并進(jìn)行數(shù)據(jù)上報(bào)。

緩存集群節(jié)點(diǎn)

每個(gè)集群的節(jié)點(diǎn)分布在不同機(jī)器上,若干個(gè)節(jié)點(diǎn)構(gòu)成一個(gè)分布式集群,去中心化,無(wú)單點(diǎn)。

Client

客戶(hù)端由業(yè)務(wù)方引入,如:Java 通過(guò) jar 包方式引入。

Proxy

對(duì)于非 Java 客戶(hù)端的業(yè)務(wù),通過(guò) Proxy 提供緩存接入和服務(wù)。

緩存集群一鍵部署

分布式集群的部署通常來(lái)說(shuō)是比較麻煩的,涉及到多臺(tái)機(jī)器、多個(gè)節(jié)點(diǎn)及一系列的動(dòng)作和配置,比如部署 Redis Cluster,就包括節(jié)點(diǎn)安裝與啟動(dòng)、節(jié)點(diǎn)握手、主從分配、插槽分配、設(shè)置復(fù)制這些步驟。

雖然官方提供了構(gòu)建集群的工具腳本,但機(jī)器推薦、節(jié)點(diǎn)安裝及配置仍然沒(méi)有自動(dòng)化,對(duì)于需要大規(guī)模部署和運(yùn)維 Redis Cluster 的企業(yè)來(lái)說(shuō),仍然不夠靈活和便捷。

因此,R2M 基于 Golang 實(shí)現(xiàn)了在 Web 控制臺(tái)一鍵完成從機(jī)器推薦、節(jié)點(diǎn)部署、集群構(gòu)建、到節(jié)點(diǎn)配置的所有動(dòng)作,這個(gè)過(guò)程中每臺(tái)機(jī)器上的 Agent 組件負(fù)責(zé)下載并安裝 RPM 包、在指定端口上啟動(dòng)實(shí)例,Manager 組件則負(fù)責(zé)完成集群構(gòu)建和節(jié)點(diǎn)配置過(guò)程。

自動(dòng)部署過(guò)程中,還有一些必要的驗(yàn)證條件和優(yōu)先規(guī)則,主要是以下幾點(diǎn):

檢查主節(jié)點(diǎn)數(shù)量和主備策略,是否滿足分布式選舉及高可用的最低配置條件:三個(gè)以上主節(jié)點(diǎn)、一主一從。

避免同一個(gè)集群多個(gè)節(jié)點(diǎn)部署在相同機(jī)器上,防止機(jī)器故障,優(yōu)先推薦符合條件的機(jī)器。

根據(jù)機(jī)器可用內(nèi)存,優(yōu)先推薦剩余可用內(nèi)存多的機(jī)器。

根據(jù)主節(jié)點(diǎn)數(shù)量,均衡分配插槽數(shù)量。

資源規(guī)劃與統(tǒng)籌管理

由于機(jī)房、機(jī)器、業(yè)務(wù)數(shù)量眾多,要做好平臺(tái)化管理,需要對(duì)資源進(jìn)行合理的事先規(guī)劃,事先規(guī)劃包括:申請(qǐng)接入時(shí)業(yè)務(wù)方對(duì)容量進(jìn)行預(yù)估,合理分配緩存實(shí)例大小和數(shù)量、機(jī)器的預(yù)留內(nèi)存,為了方便管理和統(tǒng)計(jì),通常還需要根據(jù)機(jī)房對(duì)機(jī)器進(jìn)行分組、或者根據(jù)業(yè)務(wù)進(jìn)行機(jī)器分組。

做好事先規(guī)劃的同時(shí),還需要對(duì)資源使用情況做到一目了然,避免出現(xiàn)超用或嚴(yán)重超配的情況。

嚴(yán)重超配,就是實(shí)際使用量遠(yuǎn)小于預(yù)估容量的情況,這種情況還是很多的,因?yàn)楹芏鄷r(shí)候容量很難準(zhǔn)確預(yù)估,或者有的業(yè)務(wù)開(kāi)發(fā)為了保險(xiǎn)起見(jiàn)或擔(dān)心不好擴(kuò)容,申請(qǐng)的容量往往遠(yuǎn)大于實(shí)際需要,對(duì)于這種情況,我們可以進(jìn)行一鍵縮容,防止資源浪費(fèi)。

對(duì)于超用,也就是機(jī)器實(shí)際資源使用量超過(guò)了標(biāo)準(zhǔn),則是要非常注意的,緩存機(jī)器超用比如 Redis 機(jī)器超用可能導(dǎo)致非常嚴(yán)重的后果,比如 OOM、發(fā)生數(shù)據(jù)淘汰、性能急劇下降,為了避免超用,需要進(jìn)行合理的資源預(yù)留、選擇合適的淘汰策略,同時(shí)平臺(tái)要有完善的監(jiān)控和實(shí)時(shí)的報(bào)警功能。

R2M 通過(guò)對(duì)機(jī)房、分組、機(jī)器、實(shí)例的層級(jí)管理和監(jiān)控,方便我們更好地對(duì)資源進(jìn)行規(guī)劃,并最大限度的統(tǒng)籌和平衡資源利用,防止資源浪費(fèi)和機(jī)器超用。

擴(kuò)容及數(shù)據(jù)遷移

業(yè)務(wù)在申請(qǐng)緩存時(shí),我們都會(huì)要求預(yù)估容量,根據(jù)預(yù)估值進(jìn)行分配,但預(yù)估值經(jīng)常是不準(zhǔn)確的,計(jì)劃也永遠(yuǎn)趕不上變化,業(yè)務(wù)場(chǎng)景拓展、業(yè)務(wù)量和數(shù)據(jù)的增長(zhǎng)總是無(wú)法預(yù)測(cè)的。

因此,良好的擴(kuò)容機(jī)制顯得尤為重要,擴(kuò)容做得好不好,決定了系統(tǒng)的擴(kuò)展性好不好。在 R2M 中,我們將水平擴(kuò)容解耦為兩個(gè)步驟,添加新節(jié)點(diǎn)和數(shù)據(jù)遷移,添加新節(jié)點(diǎn)其實(shí)就是一個(gè)自動(dòng)部署的過(guò)程,很多步驟與集群創(chuàng)建過(guò)程是相同的,那么關(guān)鍵就在于如何解決數(shù)據(jù)遷移問(wèn)題了。

數(shù)據(jù)遷移主要解決以下問(wèn)題:

如何做到不影響業(yè)務(wù)的正常讀寫(xiě),即業(yè)務(wù)方對(duì)遷移是基本無(wú)感知的?

如何保證遷移的速度?

當(dāng)一條數(shù)據(jù)處于遷移中間狀態(tài)時(shí),如果此時(shí)客戶(hù)端需要對(duì)該數(shù)據(jù)進(jìn)行讀寫(xiě),如何處理?

遷移過(guò)程中收到讀操作,是讀源節(jié)點(diǎn)還是目標(biāo)節(jié)點(diǎn),如何確??蛻?hù)端不會(huì)讀到臟數(shù)據(jù)?

遷移過(guò)程中是寫(xiě)源節(jié)點(diǎn)還是寫(xiě)目標(biāo)節(jié)點(diǎn),如果確保不寫(xiě)錯(cuò)地方、不會(huì)由于遷移使得新值被舊值覆蓋?

遷移完成,如何通知客戶(hù)端,將讀寫(xiě)請(qǐng)求路由到新節(jié)點(diǎn)上?

為了解決這些問(wèn)題,我們充分吸取了 Redis Cluster 的優(yōu)點(diǎn),同時(shí)也解決了它的一些不足之處和缺點(diǎn)。

數(shù)據(jù)遷移原理

上圖就是 Redis Cluster 的數(shù)據(jù)遷移原理圖,遷移過(guò)程是通過(guò)源節(jié)點(diǎn)、目標(biāo)節(jié)點(diǎn)、客戶(hù)端三者共同配合完成的。

Redis Cluster 將數(shù)據(jù)集分為 16384 個(gè)插槽,插槽是數(shù)據(jù)分割的最小單位,所以數(shù)據(jù)遷移時(shí)最少要遷移一個(gè)插槽,遷移的最小粒度是一個(gè) key,對(duì)每個(gè) key 的遷移可以看成是一個(gè)原子操作,會(huì)短暫阻塞源節(jié)點(diǎn)和目標(biāo)節(jié)點(diǎn)上對(duì)該 key 的讀寫(xiě),這樣就使得不會(huì)出現(xiàn)遷移“中間狀態(tài)”,即 key 要么在源節(jié)點(diǎn),要么在目標(biāo)節(jié)點(diǎn)。

如上圖,假設(shè)正在遷移 9 號(hào)插槽,首先會(huì)在源節(jié)點(diǎn)中將 9 號(hào)插槽標(biāo)記為“正在遷移”狀態(tài),將目標(biāo)節(jié)點(diǎn)中 9 號(hào)插槽標(biāo)記為“正在導(dǎo)入”狀態(tài),然后遍歷遷移該插槽中所有的 key。

此時(shí),如果客戶(hù)端要對(duì) 9 號(hào)插槽的數(shù)據(jù)進(jìn)行訪問(wèn),如果該數(shù)據(jù)還沒(méi)被遷移到目標(biāo)節(jié)點(diǎn),則直接讀寫(xiě)并返回,如果該數(shù)據(jù)已經(jīng)被遷移到目標(biāo)節(jié)點(diǎn),則返回 Ask 響應(yīng)并攜帶目標(biāo)節(jié)點(diǎn)的地址,告訴客戶(hù)端到目標(biāo)節(jié)點(diǎn)上再請(qǐng)求一次。

如果 9 號(hào)插槽的數(shù)據(jù)被全部遷移完成,客戶(hù)端還繼續(xù)到源節(jié)點(diǎn)進(jìn)行讀寫(xiě)的話,則返回 Moved 響應(yīng)給客戶(hù)端,客戶(hù)端收到 Moved 響應(yīng)后可以重新獲取集群狀態(tài)并更新插槽信息,后續(xù)對(duì) 9 號(hào)插槽的訪問(wèn)就會(huì)到新節(jié)點(diǎn)上進(jìn)行。這樣,就完成了對(duì)一個(gè)插槽的遷移。

多節(jié)點(diǎn)并行數(shù)據(jù)遷移

Redis Cluster 是基于 CRC16 算法做 hash 分片的,在插槽數(shù)量差不多且沒(méi)有大 key 存在的情況下,各節(jié)點(diǎn)上數(shù)據(jù)的分布通常非常均衡,而由于數(shù)據(jù)遷移是以 key 為單位的,遷移速度較慢。

當(dāng)數(shù)據(jù)量暴漲需要緊急擴(kuò)容時(shí),如果一個(gè)接一個(gè)地進(jìn)行主節(jié)點(diǎn)數(shù)據(jù)遷移,有可能部分節(jié)點(diǎn)還沒(méi)來(lái)得及遷移就已經(jīng)把內(nèi)存“撐爆”了,導(dǎo)致發(fā)生數(shù)據(jù)淘汰或機(jī)器 OOM。

因此,R2M 實(shí)現(xiàn)了多節(jié)點(diǎn)并行數(shù)據(jù)遷移,防止此類(lèi)問(wèn)題發(fā)生,同時(shí)也使數(shù)據(jù)遷移耗時(shí)大大縮短,另外,結(jié)合 Redis 3.0.7 之后的 pipeline 遷移功能,可以進(jìn)一步減少網(wǎng)絡(luò)交互次數(shù),縮短遷移耗時(shí)。

可控的數(shù)據(jù)遷移

水平擴(kuò)容添加新節(jié)點(diǎn)后,為了做數(shù)據(jù) / 負(fù)載均衡,需要把部分?jǐn)?shù)據(jù)遷移到新節(jié)點(diǎn)上,通常數(shù)據(jù)遷移過(guò)程是比較耗時(shí)的,根據(jù)網(wǎng)絡(luò)條件、實(shí)例大小和機(jī)器配置的不同,可能持續(xù)幾十分鐘至幾個(gè)小時(shí)。

而數(shù)據(jù)遷移時(shí)可能對(duì)網(wǎng)絡(luò)造成較大的壓力,另外,對(duì)于正在遷移的 slot 或 keys,Redis Cluster 通過(guò) ASK 或 MOVED 重定向機(jī)制告訴客戶(hù)端將請(qǐng)求路由至新節(jié)點(diǎn),使得客戶(hù)端與 Redis 多發(fā)生一次請(qǐng)求響應(yīng)交互。

并且通??蛻?hù)端的緩存讀寫(xiě)超時(shí)比較短 (通常在 100~500ms 以?xún)?nèi)),在多重因素的作用下,有可能造成大量讀寫(xiě)超時(shí)情況,對(duì)在線業(yè)務(wù)造成較大的影響。

基于此,我們實(shí)現(xiàn)了遷移任務(wù)暫停和遷移任務(wù)繼續(xù),當(dāng)發(fā)現(xiàn)遷移影響業(yè)務(wù)時(shí),隨時(shí)可以暫停遷移,待業(yè)務(wù)低峰期再繼續(xù)進(jìn)行剩余的數(shù)據(jù)遷移,做到靈活可控。

自動(dòng)擴(kuò)容

R2M 還提供了自動(dòng)擴(kuò)容機(jī)制,開(kāi)啟自動(dòng)擴(kuò)容后,當(dāng)機(jī)器可用內(nèi)存足夠時(shí),如果實(shí)例已使用容量達(dá)到或超過(guò)了預(yù)設(shè)的閥值,則自動(dòng)對(duì)容量進(jìn)行擴(kuò)大。

對(duì)于一些比較重要的業(yè)務(wù),或不能淘汰數(shù)據(jù)的業(yè)務(wù),可以開(kāi)啟自動(dòng)擴(kuò)容。當(dāng)然,自動(dòng)擴(kuò)容也是有條件的,比如不能無(wú)限制的自動(dòng)擴(kuò)容,實(shí)例大小達(dá)到一個(gè)比較高的值時(shí),則拒絕自動(dòng)擴(kuò)容,還要預(yù)留出一部分內(nèi)存進(jìn)行 Fork 操作,避免機(jī)器發(fā)生 OOM。

數(shù)據(jù)冷熱交換存儲(chǔ)

由于我們線上存在很多大容量 (幾百 GB 或幾個(gè) TB) 緩存集群,緩存機(jī)器內(nèi)存成本巨大,線上機(jī)器總內(nèi)存容量已達(dá)到 66TB 左右。

經(jīng)過(guò)調(diào)研發(fā)現(xiàn),主流 DDR3 內(nèi)存和主流 SATA SSD 的單位成本價(jià)格差距大概在 20 倍左右,為了優(yōu)化基礎(chǔ)設(shè)施 (硬件、機(jī)房機(jī)柜、耗電等) 綜合成本,因此,我們考慮實(shí)現(xiàn)基于熱度統(tǒng)計(jì)的數(shù)據(jù)分級(jí)存儲(chǔ)及數(shù)據(jù)在 RAM/FLASH 之間的動(dòng)態(tài)交換,從而大幅度降低基礎(chǔ)設(shè)施綜合成本,并達(dá)到性能與成本的高平衡。

R2M 的冷熱交換存儲(chǔ)的基本思想是:基于 key 訪問(wèn)次數(shù) (LFU) 的熱度統(tǒng)計(jì)算法識(shí)別出熱點(diǎn)數(shù)據(jù),并將熱點(diǎn)數(shù)據(jù)保留在 Redis 中,對(duì)于無(wú)訪問(wèn) / 訪問(wèn)次數(shù)少的數(shù)據(jù)則轉(zhuǎn)存到 SSD 上,如果 SSD 上的 key 再次變熱,則重新將其加載到 Redis 內(nèi)存中。

思想很簡(jiǎn)單,但實(shí)際上做起來(lái)就不那么容易了,由于讀寫(xiě) SATA SSD 相對(duì)于 Redis 讀寫(xiě)內(nèi)存的速度還是有很大差距的,設(shè)計(jì)時(shí)為了避免這種性能上的不對(duì)等拖累整個(gè)系統(tǒng)的性能、導(dǎo)致響應(yīng)時(shí)間和整體吞吐量的急劇下降,我們采用了多進(jìn)程異步非阻塞模型來(lái)保證 Redis 層的高性能,通過(guò)精心設(shè)計(jì)的硬盤(pán)數(shù)據(jù)存儲(chǔ)格式、多版本 key 惰性刪除、多線程讀寫(xiě) SSD 等機(jī)制來(lái)最大限度的發(fā)揮 SSD 的讀寫(xiě)性能。

多進(jìn)程異步模型,主要是兩個(gè)進(jìn)程,一個(gè)是 SSD 讀寫(xiě)進(jìn)程,用于訪問(wèn) SSD 中的 key,一個(gè)是深度改造過(guò)的 Redis 進(jìn)程,用于讀寫(xiě)內(nèi)存 key,同時(shí)如果發(fā)現(xiàn)要讀寫(xiě)的 key 是在 SSD 上,則會(huì)將請(qǐng)求轉(zhuǎn)發(fā)給 SSD 讀寫(xiě)進(jìn)程進(jìn)行處理。

Redis 進(jìn)程這一層,最開(kāi)始我們其實(shí)是基于 Redis 3.2 做的,但 Redis 4 出了 RC 版本之后尤其是支持了 LFU、Psync2、內(nèi)存碎片整理等功能,我們就果斷的切到 Redis 4 上進(jìn)行改造開(kāi)發(fā)了。

SSD 讀寫(xiě)進(jìn)程,起初是基于開(kāi)源的 SSDB 進(jìn)行開(kāi)發(fā),不過(guò)由于 SSDB 的主從復(fù)制實(shí)現(xiàn)性能很差,數(shù)據(jù)存儲(chǔ)格式設(shè)計(jì)還不夠好,與 Redis API 有很多的不兼容問(wèn)題,最終除了基本的網(wǎng)絡(luò)框架外,基本重寫(xiě)了 SSDB。

另外由于 SSDB 默認(rèn)采用的存儲(chǔ)引擎是 leveldb,結(jié)合功能特性、項(xiàng)目活躍度等方面的原因,我們改成了比較流行的 RocksDB,當(dāng)然,其實(shí)它也是發(fā)源于 leveldb 的。

目前我們內(nèi)部已完成了該項(xiàng)目的開(kāi)發(fā),并進(jìn)行了全面的功能、穩(wěn)定性和性能測(cè)試,即將上線。由于冷熱交換存儲(chǔ)涉及的內(nèi)容較多,由于篇幅原因,這里不再詳述。該項(xiàng)目已命名為 swapdb,并在 Github 開(kāi)源

https://github.com/JRHZRD/swapdb

歡迎有興趣的同學(xué)關(guān)注,歡迎加 star~

多機(jī)房切換及容災(zāi)支持

多機(jī)房切換是一個(gè)從上到下各組件協(xié)調(diào)聯(lián)動(dòng)的過(guò)程,要考慮的因素非常多,在 R2M 中,主要包括緩存集群、路由服務(wù) (如:ZooKeeper、etcd) 及各個(gè)組件 (Manager、Web console、客戶(hù)端) 的多機(jī)房切換。

數(shù)據(jù)層的多機(jī)房切換——即緩存服務(wù)的多機(jī)房切換

對(duì)于多機(jī)房支持,關(guān)鍵在于如何避免腦裂問(wèn)題。我們先來(lái)看看腦裂是如何發(fā)生的。

正常情況下,一個(gè)緩存集群的節(jié)點(diǎn)部署在同一個(gè)機(jī)房,按最低三主、一主一從進(jìn)行部署,每個(gè)主節(jié)點(diǎn)負(fù)責(zé)一部分?jǐn)?shù)據(jù),如果主節(jié)點(diǎn)掛了,剩余主節(jié)點(diǎn)通過(guò)選舉將它的從節(jié)點(diǎn)提升為新的主節(jié)點(diǎn),即自動(dòng) failover。

如果要進(jìn)行機(jī)房切換或?qū)χ匾臉I(yè)務(wù)進(jìn)行多機(jī)房容災(zāi),則需要在另一個(gè)機(jī)房對(duì)每個(gè)主節(jié)點(diǎn)再添加一個(gè)從節(jié)點(diǎn),那么每個(gè)主節(jié)點(diǎn)將有兩個(gè)從,運(yùn)行過(guò)程中,如果集群中有節(jié)點(diǎn)發(fā)生自動(dòng) failover,主節(jié)點(diǎn)可能被 failover 到另一個(gè)機(jī)房,結(jié)果,就會(huì)出現(xiàn)同一個(gè)集群的主節(jié)點(diǎn)分布在不同機(jī)房的情況。

這種情況下,如果機(jī)房間網(wǎng)絡(luò)鏈路出現(xiàn)問(wèn)題,A 機(jī)房的主節(jié)點(diǎn)與 B 機(jī)房的主節(jié)點(diǎn)會(huì)互相認(rèn)為對(duì)方處于 fail 狀態(tài),假設(shè)多數(shù)主節(jié)點(diǎn)都在 A 機(jī)房,那么 A 機(jī)房的主節(jié)點(diǎn)會(huì)從同機(jī)房的從節(jié)點(diǎn)中選出新的主節(jié)點(diǎn)來(lái)替代 B 機(jī)房的主節(jié)點(diǎn),導(dǎo)致相同的分片同時(shí)被分屬兩個(gè)機(jī)房的主節(jié)點(diǎn)負(fù)責(zé),A 機(jī)房的客戶(hù)端把這些分片的數(shù)據(jù)寫(xiě)到了 A 機(jī)房新選出的主節(jié)點(diǎn),B 機(jī)房的客戶(hù)端仍然把數(shù)據(jù)寫(xiě)到了 B 機(jī)房的主節(jié)點(diǎn)上,從而造成腦裂導(dǎo)致數(shù)據(jù)無(wú)法合并。

熟悉 Redis Cluster 的同學(xué)可能知道,Redis Cluster 有一個(gè)cluster-require-full-coverage的參數(shù),默認(rèn)是開(kāi)啟的,該參數(shù)的作用是:只要有節(jié)點(diǎn)宕機(jī)導(dǎo)致 16384 個(gè)分片沒(méi)被全覆蓋,整個(gè)集群就拒絕服務(wù),大大降低可用性,所以實(shí)際應(yīng)用中一定要關(guān)閉。但帶來(lái)的問(wèn)題就是,如果出現(xiàn)上述問(wèn)題,就會(huì)造成腦裂帶來(lái)的后果。

為了解決這個(gè)問(wèn)題,我們?cè)?Redis 的集群通信消息中加入了datacenter標(biāo)識(shí),收到自動(dòng) failover 請(qǐng)求時(shí),主節(jié)點(diǎn)會(huì)將自己的 datacenter 標(biāo)識(shí)與被提名做 failover 的從節(jié)點(diǎn)的 datacenter 標(biāo)識(shí)進(jìn)行比較,如果一致,則同意該節(jié)點(diǎn)的自動(dòng) failover 請(qǐng)求,否則,拒絕該請(qǐng)求。

保證自動(dòng) failover 只會(huì)發(fā)生在同機(jī)房從節(jié)點(diǎn)上,避免主節(jié)點(diǎn)分布在多個(gè)機(jī)房的情況。而對(duì)于手動(dòng) failover 或強(qiáng)制 failover,則不受此限制。針對(duì) Redis 多機(jī)房支持的功能,已經(jīng)向 Redis 官方提交了 pull request,作者 antirez 大神在 Redis 4.2 roadmap 中表示會(huì)加入這塊功能。

目前,R2M 的機(jī)房正常切換及容災(zāi)切換都已經(jīng)實(shí)現(xiàn)了在 Web console 的一鍵切換。當(dāng)需要做機(jī)房正常維護(hù)或遷移時(shí),就可以通過(guò)手動(dòng) failover 將主節(jié)點(diǎn)批量切到跨機(jī)房的從節(jié)點(diǎn)上。

值得一提的是,正常切換過(guò)程保證切換前后主從節(jié)點(diǎn)的數(shù)據(jù)一致。當(dāng)機(jī)房由于斷電或其它原因出故障時(shí),通過(guò)強(qiáng)制 failover 將主節(jié)點(diǎn)批量切到跨機(jī)房的從節(jié)點(diǎn)上,由于是突發(fā)事件,少量數(shù)據(jù)可能還未同步給從節(jié)點(diǎn),但這里的主要目的是容災(zāi)、及時(shí)恢復(fù)服務(wù),可用性重要程度要遠(yuǎn)大于少量的數(shù)據(jù)不一致或丟失。

系統(tǒng)組件的多機(jī)房切換

緩存集群路由服務(wù) (ZK) 的多機(jī)房切換

業(yè)務(wù)客戶(hù)端通過(guò)路由服務(wù)拿到對(duì)應(yīng)的緩存節(jié)點(diǎn)地址,在我們的生產(chǎn)環(huán)境中,每個(gè)機(jī)房的 ZK 都是獨(dú)立部署的,即不同機(jī)房的 ZK 實(shí)例屬于不同的 ZK 集群,同時(shí)每個(gè)機(jī)房的業(yè)務(wù)客戶(hù)端直接訪問(wèn)本機(jī)房的 ZK 獲取緩存節(jié)點(diǎn)。

在 R2M 中,每個(gè)機(jī)房的 ZK 路由節(jié)點(diǎn)存儲(chǔ)的配置全部相同,我們保持 ZK 路由節(jié)點(diǎn)中的信息盡量簡(jiǎn)單,所有集群狀態(tài)相關(guān)的東西都不在 ZK 上,基本是靜態(tài)配置,只有擴(kuò)容或下線節(jié)點(diǎn)時(shí)才需要更改配置。所以,當(dāng)機(jī)房切換時(shí),不需要對(duì) ZK 做任何變更。

客戶(hù)端的多機(jī)房切換

業(yè)務(wù)客戶(hù)端本身是多機(jī)房部署的,不存在多機(jī)房問(wèn)題,但客戶(hù)端需要在緩存集群發(fā)生多機(jī)房切換后及時(shí)把服務(wù)路由到切換后的機(jī)房,這就需要通知分布于多個(gè)機(jī)房的各個(gè)客戶(hù)端,并與每個(gè)客戶(hù)端維持或建立連接,無(wú)疑是一大麻煩事。

在 R2M 中,由于客戶(hù)端是 smart client,當(dāng)感知到異常時(shí),可以從存活的節(jié)點(diǎn)中重新獲取集群狀態(tài),自動(dòng)感知節(jié)點(diǎn)角色變更并切換,也就不存在通知的問(wèn)題了。

Manager 組件的多機(jī)房切換

Manager 是緩存集群的管理組件,運(yùn)維操作包括機(jī)房切換操作都是通過(guò)它來(lái)進(jìn)行,所以必須做到 Manager 本身的多機(jī)房才能保證可以隨時(shí)進(jìn)行機(jī)房容災(zāi)切換或正常切換。

需要注意的是,由于不同機(jī)房的 ZK 是獨(dú)立的,Manager 的多機(jī)房切換不能直接依賴(lài)于 ZK 來(lái)實(shí)現(xiàn),因?yàn)橛锌赡軇偤帽灰蕾?lài)的那個(gè) ZK 所在的機(jī)房掛掉了,所以,我們實(shí)現(xiàn)了 Manager 的多機(jī)房選舉 (類(lèi) Raft 機(jī)制) 功能,多個(gè) Manager 可以自行選舉 leader、自動(dòng) failover。

Web console 的多機(jī)房切換

對(duì)于 Web console 的多機(jī)房切換,這個(gè)就相對(duì)簡(jiǎn)單了。由于 Web 是無(wú)狀態(tài)的,直接通過(guò) Nginx 做負(fù)載均衡,只要有任意一個(gè)機(jī)房的 Web 組件可用就可以了。

監(jiān)控、告警及問(wèn)題排查

業(yè)務(wù)出現(xiàn)調(diào)用超時(shí)、性能指標(biāo)出現(xiàn)抖動(dòng)怎么辦?

一次業(yè)務(wù)調(diào)用可能涉及多個(gè)服務(wù),比如消息隊(duì)列、數(shù)據(jù)庫(kù)、緩存、RPC,如何確認(rèn)不是緩存的問(wèn)題?

如果是緩存服務(wù)的問(wèn)題,是機(jī)器問(wèn)題、網(wǎng)絡(luò)問(wèn)題、系統(tǒng)問(wèn)題、還是業(yè)務(wù)使用不當(dāng)問(wèn)題?

當(dāng)出現(xiàn)上述問(wèn)題時(shí),面對(duì)大量機(jī)器、集群以及無(wú)數(shù)的實(shí)例,如果沒(méi)有一套完善的監(jiān)控與告警系統(tǒng),沒(méi)有一個(gè)方便易用的可視化操作界面,那只能茫然無(wú)措、一籌莫展,耐心地一個(gè)一個(gè)實(shí)例的看日志、查 info 輸出,查網(wǎng)絡(luò)、查機(jī)器,所謂人肉運(yùn)維。

因此,R2M 提供了各種維度的監(jiān)控指標(biāo)和告警功能,及時(shí)對(duì)異常情況進(jìn)行預(yù)警,當(dāng)問(wèn)題出現(xiàn)時(shí),也能夠快速定位排查方向。

機(jī)器和網(wǎng)絡(luò)指標(biāo)

每臺(tái)機(jī)器的網(wǎng)絡(luò) QoS(丟包率、包重傳次數(shù)、發(fā)送接收錯(cuò)誤數(shù))、流入流出帶寬、CPU、內(nèi)存使用量、磁盤(pán)讀寫(xiě)速率等。

系統(tǒng)參數(shù)指標(biāo)

每個(gè)節(jié)點(diǎn)的內(nèi)存使用量、網(wǎng)絡(luò)流入流出流量、TPS、查詢(xún)命中率、客戶(hù)端 TCP 連接數(shù)、key 淘汰數(shù)、慢查詢(xún)命令記錄、實(shí)例運(yùn)行日志等。

實(shí)時(shí)監(jiān)控及歷史統(tǒng)計(jì)圖表

實(shí)時(shí)和歷史統(tǒng)計(jì)圖表是對(duì)各項(xiàng)參數(shù)指標(biāo)的實(shí)時(shí)反饋和歷史走勢(shì)的直觀展示,不僅能在出問(wèn)題時(shí)快速給出定位的方向,還能夠?qū)\(yùn)維和業(yè)務(wù)開(kāi)發(fā)提供非常有價(jià)值的參考數(shù)據(jù),這些參考數(shù)據(jù)也反過(guò)來(lái)促進(jìn)業(yè)務(wù)系統(tǒng)進(jìn)行優(yōu)化、促進(jìn)更好地運(yùn)維。

對(duì)于實(shí)例的歷史監(jiān)控統(tǒng)計(jì)數(shù)據(jù),由每個(gè)機(jī)器上部署的 Agent 組件負(fù)責(zé)收集并上報(bào)。由于實(shí)例數(shù)及監(jiān)控項(xiàng)非常多,監(jiān)控?cái)?shù)據(jù)量可能會(huì)非常大。

為了避免監(jiān)控?cái)?shù)據(jù)占用大量數(shù)據(jù)庫(kù)空間,對(duì)于歷史統(tǒng)計(jì)數(shù)據(jù),我們會(huì)保留展示最近 12 小時(shí)以分鐘為維度、最近一個(gè)月以小時(shí)為維度、以及最近一年以天為維度的數(shù)據(jù),12 小時(shí)以前的分鐘數(shù)據(jù)自動(dòng)合并為小時(shí)維度的數(shù)據(jù),一個(gè)月以前的小時(shí)數(shù)據(jù)自動(dòng)合并為天維度的數(shù)據(jù),在合并時(shí),保留這個(gè)時(shí)間段的指標(biāo)最高值、最低值、以及累加后的平均值。

對(duì)于實(shí)時(shí)監(jiān)控圖表,則是用戶(hù)請(qǐng)求查看時(shí)才與對(duì)應(yīng)的緩存實(shí)例建立連接,直接從實(shí)例獲取實(shí)時(shí)信息。

客戶(hù)端性能指標(biāo)

每個(gè)客戶(hù)端的 TP50、TP90、TP99、TP999 請(qǐng)求耗時(shí)統(tǒng)計(jì),快速定位問(wèn)題 IP。

告警項(xiàng)

容量告警、流入流出流量、TPS、實(shí)例阻塞、實(shí)例停止服務(wù)、客戶(hù)端連接數(shù)等。

總 ? 結(jié)

限于篇幅原因,這里只能對(duì) R2M 緩存系統(tǒng)大致的設(shè)計(jì)思路和功能做一個(gè)簡(jiǎn)要的介紹,很多細(xì)節(jié)和原理性的東西沒(méi)能一一詳述,比如數(shù)據(jù)的冷熱交換,實(shí)際上做這個(gè)東西的過(guò)程中我們遇到了很多的挑戰(zhàn),也希望以后能對(duì)這塊做詳細(xì)的介紹。最后,也希望有更多的技術(shù)同仁擁抱和參與開(kāi)源,讓大家有更多更好的輪子用。

作者介紹

李竟成(微信號(hào):lijingcheng87),任職于京東金融杭州研發(fā)中心,京東金融 R2M 分布式緩存系統(tǒng)負(fù)責(zé)人,略通 Redis 內(nèi)核,愛(ài)好開(kāi)源,關(guān)注 KV 存儲(chǔ)、分布式架構(gòu)及數(shù)據(jù)一致性相關(guān)領(lǐng)域。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容