新聞中心
【稿件】在高并發(fā)場(chǎng)景下,很多人都把 Cache(高速緩沖存儲(chǔ)器)當(dāng)做可以“續(xù)命”的靈丹妙藥,哪里高并發(fā)壓力大,哪里就上傳 Cache 來(lái)解決并發(fā)問(wèn)題。

但有時(shí)候,即使使用了 Cache,卻發(fā)現(xiàn)系統(tǒng)依然卡頓宕機(jī),是因?yàn)?Cache 技術(shù)不好嗎?非也,其實(shí)這是緩存的治理工作沒(méi)有做好。
2018 年 5 月 18-19 日,由 主辦的全球軟件與運(yùn)維技術(shù)峰會(huì)在北京召開。
在 19 日下午“高并發(fā)與實(shí)時(shí)處理”分會(huì)場(chǎng),同程藝龍機(jī)票事業(yè)群 CTO 王曉波帶來(lái)了《高并發(fā)場(chǎng)景的緩存治理》的主題演講。
他針對(duì)如何讓緩存更適合高并發(fā)使用、如何正確使用緩存、如何通過(guò)治理化解緩存問(wèn)題等熱點(diǎn)展開了闡述。
對(duì)于我們來(lái)說(shuō),我們是 OTA 的角色,所以有大量的數(shù)據(jù)要計(jì)算處理變?yōu)榭墒圪u商品,總的來(lái)說(shuō)是“商品搬運(yùn)工,而非生產(chǎn)商”。
所以面對(duì)各種大數(shù)據(jù)并發(fā)運(yùn)行的應(yīng)用場(chǎng)景,我們需要通過(guò)各種緩存技術(shù)來(lái)提升服務(wù)的質(zhì)量。
想必大家都聽說(shuō)過(guò)服務(wù)的治理和數(shù)據(jù)的治理,那么是否聽說(shuō)過(guò)緩存的治理呢?誠(chéng)然,在許多場(chǎng)景下,Cache 成了應(yīng)對(duì)各處出現(xiàn)高并發(fā)問(wèn)題的一顆“銀彈”。
但是它并非是放之四海皆準(zhǔn)的,有時(shí)它反而成了一顆導(dǎo)致系統(tǒng)“掛掉”的自殺子彈。有時(shí)候這種原因的出現(xiàn)并非 Cache 本身的技術(shù)不好,而是我們沒(méi)有做好治理。
下面,我們將從三個(gè)方面來(lái)具體討論緩存的治理:
- 緩存使用中的一些痛點(diǎn)
- 如何用好緩存,用正確緩存
- 如何通過(guò)治理讓緩存的問(wèn)題化為無(wú)形
緩存使用中的一些痛點(diǎn)
我們同程的業(yè)務(wù)特點(diǎn)是:OTA 類商品,沒(méi)有任何一個(gè)價(jià)格是固定的。像酒店,客戶今天訂、明天訂、連續(xù)訂三天、訂兩天,是否跨周末,他們最后得出的價(jià)格都是不一樣的。
價(jià)格隨著時(shí)間的變化而波動(dòng)的。這些波動(dòng)會(huì)引發(fā)大量的計(jì)算,進(jìn)而帶來(lái)性能上的損耗。
要解決性能的損耗問(wèn)題,我們勢(shì)必要插入各種 Cache,包括:價(jià)格的 Cache、時(shí)間段的 Cache、庫(kù)存的 Cache。而且這些 Cache 的寫入數(shù)據(jù)量遠(yuǎn)大于整個(gè)外部的請(qǐng)求數(shù)據(jù)量,即:寫多于讀。
下面介紹同程緩存使用的歷史:
- 一開始,我們僅使用一臺(tái) Memcache 來(lái)提供緩存服務(wù)。
- 后來(lái),我們發(fā)現(xiàn) Memcache 存在著支持并發(fā)性不好、可運(yùn)維性欠佳、原子性操作不夠、在誤操作時(shí)產(chǎn)生數(shù)據(jù)不一致等問(wèn)題。
- 因此,我們轉(zhuǎn)為使用 Redis,以單線程保證原子性操作,而且它的數(shù)據(jù)類型也比較多。當(dāng)有一批新的業(yè)務(wù)邏輯被寫到 Redis 中時(shí),我們就把它當(dāng)作一個(gè)累加計(jì)數(shù)器。
當(dāng)然,更有甚者把它當(dāng)作數(shù)據(jù)庫(kù)。由于數(shù)據(jù)庫(kù)比較慢,他們就讓數(shù)據(jù)線先寫到 Redis,再落盤到數(shù)據(jù)庫(kù)中。
- 隨后,我們發(fā)現(xiàn)在單機(jī) Redis 的情況下,Cache 成了系統(tǒng)的“命門”。哪怕上層的計(jì)算尚屬良好、哪怕流量并不大,我們的服務(wù)也會(huì)“掛掉”。于是我們引入了集群 Redis。
- 同時(shí),我們用 Java 語(yǔ)言自研了 Redis 的客戶端。我們也在客戶端里實(shí)現(xiàn)了二級(jí) Cache。不過(guò),我們發(fā)現(xiàn)還是會(huì)偶爾出現(xiàn)錯(cuò)亂的問(wèn)題。
- 后來(lái),我們還嘗試了分布式 Cache,以及將 Redis 部署到 Docker 里面。
最終,我們發(fā)現(xiàn)這些問(wèn)題都是跟場(chǎng)景相關(guān)。如果你所構(gòu)建的場(chǎng)景較為紊亂,則直接會(huì)導(dǎo)致底層無(wú)法提供服務(wù)。
下面我們來(lái)看看有哪些需要治理的場(chǎng)景,通俗地說(shuō)就是有哪些“坑”需要“填”。
早期在單機(jī)部署 Redis 服務(wù)的時(shí)代,我們針對(duì)業(yè)務(wù)系統(tǒng)部署了一套使用腳本運(yùn)維的平臺(tái)。
當(dāng)時(shí)在一臺(tái)虛機(jī)上能跑六萬(wàn)左右的并發(fā)數(shù)據(jù),這些對(duì)于 Redis 服務(wù)器來(lái)說(shuō)基本夠用了。
但是當(dāng)大量部署,并達(dá)到了數(shù)百多臺(tái)時(shí),我們碰到了兩個(gè)問(wèn)題:
- 面對(duì)高并發(fā)的性能需求,我們無(wú)法單靠腳本進(jìn)行運(yùn)維。一旦運(yùn)維操作出現(xiàn)失誤或失控,就可能導(dǎo)致 Redis 的主從切換失敗,甚至引起服務(wù)宕機(jī),從而直接對(duì)整個(gè)業(yè)務(wù)端產(chǎn)生影響。
- 應(yīng)用調(diào)用的凌亂。在采用微服務(wù)化之前,我們面對(duì)的往往是一個(gè)擁有各種模塊的大系統(tǒng)。
而在使用場(chǎng)景中,我們常把 Redis 看成數(shù)據(jù)庫(kù)、存有各種工程的數(shù)據(jù)源。同時(shí)我們將 Cache 視為一個(gè)黑盒子,將各種應(yīng)用數(shù)據(jù)都放入其中。
例如,對(duì)于一個(gè)訂單交易系統(tǒng),你可能會(huì)把訂單積分、訂單說(shuō)明、訂單數(shù)量等信息放入其中,這樣就導(dǎo)致了大量的業(yè)務(wù)模塊被耦合于此,同時(shí)所有的業(yè)務(wù)邏輯數(shù)據(jù)塊也集中在了 Redis 處。
那么就算我們?nèi)ゲ鸱治⒎?wù)、做代碼解耦,可是多數(shù)情況下緩存池中的大數(shù)據(jù)并沒(méi)有得到解耦,多個(gè)服務(wù)端仍然通過(guò) Redis 去共享和調(diào)用數(shù)據(jù)。
一旦出現(xiàn)宕機(jī),就算你能對(duì)服務(wù)進(jìn)行降級(jí),也無(wú)法對(duì)數(shù)據(jù)本身采取降級(jí),從而還是會(huì)導(dǎo)致整體業(yè)務(wù)的“掛掉”。
脆弱的數(shù)據(jù)消失了。由于大家都習(xí)慣把 Redit 當(dāng)作數(shù)據(jù)庫(kù)使用(雖然大家都知道在工程中不應(yīng)該如此),畢竟它不是數(shù)據(jù)庫(kù),沒(méi)有持久性,所以一旦數(shù)據(jù)丟失就會(huì)出現(xiàn)大的麻煩。
為了防止單臺(tái)掛掉,我們可以采用多臺(tái) Redis。此時(shí)運(yùn)維和應(yīng)用分別有兩種方案:
- 運(yùn)維認(rèn)為:可以做“主從”,并提供一個(gè)浮動(dòng)的虛擬 IP(VIP)地址。在一個(gè)節(jié)點(diǎn)出現(xiàn)問(wèn)題時(shí),VIP 地址不用變更,直接連到下一個(gè)節(jié)點(diǎn)便可。
- 應(yīng)用認(rèn)為:可以在應(yīng)用客戶端里寫入兩個(gè)地址,并采取“哨兵”監(jiān)控,來(lái)實(shí)現(xiàn)自動(dòng)切換。
這兩個(gè)方案看似沒(méi)有問(wèn)題,但是架不住 Redis 的濫用。我們?cè)?jīng)碰到過(guò)一個(gè)現(xiàn)實(shí)的案例:如上圖右下角所示,兩個(gè) Redis 根據(jù)主從關(guān)系可以互相切換。
按照需求,存有 20G 數(shù)據(jù)的主 Redis 開始對(duì)從 Redis 進(jìn)行同步。此時(shí)網(wǎng)絡(luò)出現(xiàn)卡頓,而應(yīng)用正好發(fā)現(xiàn)自己的請(qǐng)求也相應(yīng)變慢了,因此上層應(yīng)用根據(jù)網(wǎng)絡(luò)故障采取主從切換。
然而此時(shí)由于主從 Redis 正好處于同步狀態(tài),資源消耗殆盡,那么在上次應(yīng)用看來(lái)此時(shí)主從 Redis 都是不可達(dá)的。
我們經(jīng)過(guò)深入排查,最終發(fā)現(xiàn)是在 Cache 中某個(gè)表的一個(gè) Key 中,被存放了 20G 的數(shù)據(jù)。
而在程序?qū)用嫔?,他們并沒(méi)有控制好該 Key 的消失時(shí)間(如一周),因而造成了該 Key 被持續(xù)追加增大的狀況。
由上可見(jiàn),就算我們對(duì) Redis 進(jìn)行了拆分,這個(gè)巨大的 Key 仍會(huì)存在于某一個(gè)“片”上。
如上圖所示,仍以 Redis 為例,我們能夠監(jiān)控的方面包括:
- 當(dāng)前客戶端的連接數(shù)
- 客戶端的輸出與輸入情況
- 是否出現(xiàn)堵塞
- 被分配的整個(gè)內(nèi)存總量
- 主從復(fù)制時(shí)的狀態(tài)信息
- 集群的情況
- 各服務(wù)器每秒執(zhí)行的命令數(shù)量
可以說(shuō),這些監(jiān)控的方面并不能及時(shí)地發(fā)現(xiàn)上述 20 個(gè) G 的 Key 數(shù)據(jù)。再比如:通常系統(tǒng)是在客戶下訂單之后,才增加會(huì)員積分。
但是在應(yīng)用設(shè)計(jì)上卻將核心訂單里的核心 Key,與本該滯后增加的積分輔助進(jìn)程,放在了同一個(gè)實(shí)例之中。
由于我們能夠監(jiān)控到的都是些延遲信息,因此這種將級(jí)別高的數(shù)據(jù)與級(jí)別低的數(shù)據(jù)混淆的情況,是無(wú)法被監(jiān)控到的。
上面是一段運(yùn)維與開發(fā)的真實(shí)對(duì)話,曾發(fā)生在我們公司內(nèi)部的 IM 上,它反映了在 DevOps 推進(jìn)之前,運(yùn)維與開發(fā)之間的矛盾。
開發(fā)問(wèn):Redis 為什么不能訪問(wèn)?
運(yùn)維答:剛才服務(wù)器因內(nèi)存故障自動(dòng)重啟了。其背后的原因是:一個(gè) Cache 的故障導(dǎo)致了某個(gè)業(yè)務(wù)的故障。業(yè)務(wù)認(rèn)為自己的代碼沒(méi)有問(wèn)題,原因在于運(yùn)維的 Cache 上。
開發(fā)問(wèn):為什么我的 Cache 的延遲這么大?
運(yùn)維答:發(fā)現(xiàn)開發(fā)在此處放了幾萬(wàn)條數(shù)據(jù),從而影響了插入排序。
開發(fā)問(wèn):我寫進(jìn)去的 Key 找不到?肯定是 Cache 出錯(cuò)了。這其實(shí)是運(yùn)維 Cache 與使用 Cache 之間的最大矛盾。
運(yùn)維答:你的 Redis 超過(guò)最大限制了,根本就沒(méi)寫成功,或者寫進(jìn)去就直接被淘汰了。這就是大家都把它當(dāng)成黑盒所帶來(lái)的問(wèn)題。
開發(fā)問(wèn):剛剛為何讀取全部失???
運(yùn)維答:網(wǎng)絡(luò)臨時(shí)中斷,在全同步完成之前,從機(jī)的讀取全部失敗了。這是一個(gè)非常經(jīng)典的問(wèn)題,當(dāng)時(shí)運(yùn)維為了簡(jiǎn)化起見(jiàn),將主從代替了集群模式。
開發(fā)問(wèn):我的系統(tǒng)需要 800G 的 Redis,何時(shí)能準(zhǔn)備好?
運(yùn)維答:我們線上的服務(wù)器最大只有 256 G。
開發(fā)問(wèn):為什么 Redis 慢得像驢一樣,是否服務(wù)器出了故障?
運(yùn)維答:對(duì)千萬(wàn)級(jí)的 Key,使用 Keys*,肯定會(huì)慢。
由上可見(jiàn)這些問(wèn)題既有來(lái)自運(yùn)維的,也有來(lái)自開發(fā)的,同時(shí)還有當(dāng)前技術(shù)所限制的。
我們?cè)趹?yīng)對(duì)并發(fā)查詢時(shí),只注重它給我們帶來(lái)的“快”這一性能特點(diǎn),卻忽略了對(duì) Cache 的使用規(guī)范,以及在設(shè)計(jì)時(shí)需要考慮到的各種本身缺點(diǎn)。
如何用好緩存,用正確緩存?
因此在某次重大故障發(fā)生之后,我們總結(jié)出:沒(méi)想到初始狀態(tài)下只有 30000 行代碼的小小 Redis 竟然能帶來(lái)如此神奇的功能。
以至于它在程序員手中變成了一把“見(jiàn)到釘子就想錘的錘子”,即:他們看見(jiàn)任何的需求都想用緩存去解決。
于是他們相繼開發(fā)出來(lái)了基于緩存的日志搜集器、倒計(jì)時(shí)、計(jì)數(shù)器、訂單系統(tǒng)等,卻忘記了它本身只是一個(gè) Cache。一旦出現(xiàn)了故障,它們將如何去保證其本身呢?
下面我們來(lái)看看緩存故障的具體因素有哪些?
過(guò)度依賴
即:明明不需要設(shè)置緩存之處,卻非要用緩存。程序員們常認(rèn)為某處可能會(huì)在將來(lái)出現(xiàn)大的并發(fā)量,故放置了緩存,卻忘記了對(duì)數(shù)據(jù)進(jìn)行隔離,以及使用的方式是否正確。
例如:在某些代碼中,一個(gè)函數(shù)會(huì)執(zhí)行一到兩百次 Cache 的讀取,通過(guò)反復(fù)的 get 操作,對(duì)同一個(gè) Key 進(jìn)行連續(xù)的讀取。
試想,一次并發(fā)會(huì)帶給 Redis 多少次操作呢?這些對(duì)于 Redis 來(lái)說(shuō)負(fù)載是相當(dāng)巨大的。
數(shù)據(jù)落盤
這是一個(gè)高頻次出現(xiàn)的問(wèn)題。由于大家確實(shí)需要一個(gè)高速的 KV 存儲(chǔ),來(lái)實(shí)現(xiàn)數(shù)據(jù)落盤需求。
因此他們都會(huì)把整個(gè) Cache 當(dāng)作數(shù)據(jù)庫(kù)去使用,將任何不允許丟失的數(shù)據(jù)都放在 Cache 之中。
即使公司有各種使用規(guī)范,此現(xiàn)象仍是無(wú)法杜絕。最終我們?cè)?Cache 平臺(tái)上真正做了一個(gè) KV 數(shù)據(jù)庫(kù)供程序員們使用,并且要求他們?cè)谑褂玫臅r(shí)候,必須聲明用的是 KV 數(shù)據(jù)庫(kù)還是 Cache。
超大容量
由于大家都知道“放到內(nèi)存里是最快的”,因此他們對(duì)于內(nèi)存的需求是無(wú)窮盡的。更有甚者,有人曾向我提出 10 個(gè) T 容量的需求,而根本不去考慮營(yíng)收上的成本。
雪崩效應(yīng)
由于我們使用的是大量依賴于緩存的數(shù)據(jù),來(lái)為并發(fā)提供支撐,一旦緩存出現(xiàn)問(wèn)題,就會(huì)產(chǎn)生雪崩效應(yīng)。
即:外面的流量還在,你卻不得不重啟整個(gè)緩存服務(wù)器,進(jìn)而會(huì)造成 Cache 被清空的情況。
由于斷絕了數(shù)據(jù)的來(lái)源,這將導(dǎo)致后端的服務(wù)連片“掛掉”。為了防止雪崩的出現(xiàn),我們會(huì)多寫一份數(shù)據(jù)到特定磁盤上。
其數(shù)據(jù)“新鮮度”可能不夠,但是當(dāng)雪崩發(fā)生時(shí),它會(huì)被加載到內(nèi)存中,以防止雪崩的下一波沖擊,從而能夠順利地過(guò)渡到我們重新將“新鮮”的數(shù)據(jù)灌進(jìn)來(lái)為止。
我們對(duì)上面提到的“坑”總結(jié)一下:
- 最厲害的是:使用者亂用、濫用和懶用。如前例所說(shuō),我們平時(shí)對(duì)于緩存到底在哪里用、怎么用、防止什么等方面考慮得實(shí)在太少。
- 運(yùn)維數(shù)千臺(tái)毫無(wú)使用規(guī)則的緩存服務(wù)器。我們常說(shuō) DevOps 的做法是讓應(yīng)用與運(yùn)維靠得更近,但是針對(duì)緩存進(jìn)行運(yùn)維時(shí),由于應(yīng)用開發(fā)都不關(guān)心里面的數(shù)據(jù),又何談相互靠近呢?
- 運(yùn)維不懂開發(fā),開發(fā)不懂運(yùn)維。這導(dǎo)致了緩存系統(tǒng)上各自為政,無(wú)法真正地應(yīng)用好 Cache。
- 緩存在無(wú)設(shè)計(jì)、無(wú)控制的情況下被使用。一般情況下 JVM 都能監(jiān)控到內(nèi)存的爆漲,并考慮是否需要回收。但是如前例所示,在出現(xiàn)了一個(gè) Key 居然有 20G 大小時(shí)、我們卻往往忽視了一個(gè) Key 在緩存服務(wù)器上的爆漲。
- 開發(fā)人員能力的不同。由于不可能要求所有的開發(fā)人員都是前端工程師,那么當(dāng)你這個(gè)團(tuán)隊(duì)里面有不同經(jīng)驗(yàn)的人員時(shí),如何讓他們能寫出同樣規(guī)范的代碼呢?
- 畢竟我們做的是工程,需要更多的人能夠保證寫出來(lái)的代碼不會(huì)發(fā)生上述的問(wèn)題。
- 太多的服務(wù)器資源被浪費(fèi)。特別是 Cache 的整體浪費(fèi)是非常巨大的。無(wú)論并發(fā)量高或低,是否真正需要,大家都在使用它的內(nèi)存。
- 例如:在我們的幾千臺(tái) Cache Server 中,最高浪費(fèi)量可達(dá) 60%。一些只有幾百或幾千 KPS 要求的系統(tǒng)或數(shù)據(jù)也被設(shè)計(jì)運(yùn)行在了 Cache 昂貴的內(nèi)存中。
- 而實(shí)際上它們可能僅僅是為了應(yīng)對(duì)一月一次、或一年一次促銷活動(dòng)的 Cache 高峰需求。
- 懶人心理,應(yīng)對(duì)變化不夠快。應(yīng)對(duì)高并發(fā)量,十個(gè)程序員有五個(gè)會(huì)說(shuō):為數(shù)據(jù)層添加 Cache,而不會(huì)真正去為架構(gòu)做長(zhǎng)遠(yuǎn)的規(guī)劃。
如何通過(guò)治理讓緩存的問(wèn)題化為無(wú)形
那么到底緩存應(yīng)當(dāng)如何被治理呢?從真正的開發(fā)哲學(xué)角度上說(shuō),我們想要的是一個(gè)百變的魔術(shù)箱,它能夠快速地自我變化與處理,而不需要開發(fā)和運(yùn)維人員擔(dān)心濫用的問(wèn)題。
另外,其他需要應(yīng)對(duì)的方面還包括:應(yīng)用對(duì)緩存大小的需求就像貪吃蛇一般,一堆孤島般的單機(jī)服務(wù)器,緩存服務(wù)運(yùn)維則像一個(gè)迷宮。
因此,我們希望構(gòu)建的是一種能適用各種應(yīng)用場(chǎng)景的緩存服務(wù),而不是冷冰冰的 Cache Server。
起初我們嘗試了各種現(xiàn)成的開源方案,但是后來(lái)發(fā)現(xiàn)它們或多或少存在著一些問(wèn)題。
例如:
- Cachecloud,對(duì)于部署和運(yùn)維方面欠佳。
- Codis,本身做了一個(gè)很大的集群,但是我們考慮到當(dāng)這么一個(gè)超大池出現(xiàn)問(wèn)題時(shí),整個(gè)團(tuán)隊(duì)在應(yīng)對(duì)上會(huì)失去靈活性。
- 例如:我們會(huì)擔(dān)心業(yè)務(wù)數(shù)據(jù)塊可能未做隔離,就被放到了池中,那么當(dāng)一個(gè)實(shí)例“掛掉”時(shí),所有的數(shù)據(jù)塊都會(huì)受到影響。
- Pika,雖然可以使用硬盤,但是部署方式很少。
- Twemproxy,只是代理見(jiàn)長(zhǎng),其他的能力欠佳。
后來(lái),我們選擇自己動(dòng)手,做了一個(gè) phoenix 的方案。整個(gè)系統(tǒng)包含了客戶端、運(yùn)維平臺(tái)、以及存儲(chǔ)擴(kuò)容等方面。
在最初期的架構(gòu)設(shè)計(jì)上,我們只讓應(yīng)用端通過(guò)簡(jiǎn)單的 SDK 去使用該系統(tǒng)。
為了避免服務(wù)端延續(xù)查找 Cache Server 的模式,我們要求應(yīng)用事先聲明其項(xiàng)目和數(shù)據(jù)場(chǎng)景,然后給系統(tǒng)分配一個(gè) Key。SDK 籍此為應(yīng)用分配一個(gè)新的或既有的緩存?zhèn)}庫(kù)。
如上圖所示,為了加快速度,我們將緩存區(qū)分出多個(gè)虛擬的邏輯池,它們對(duì)于上層調(diào)度系統(tǒng)來(lái)說(shuō)就是一個(gè)個(gè)的場(chǎng)景。
那么應(yīng)用就可以籍此申請(qǐng)包含需要存放何種數(shù)據(jù)的場(chǎng)景,最后根據(jù)所分配到的 Key 進(jìn)行調(diào)用。
此處,底層是各種數(shù)據(jù)的復(fù)制和遷移,而兩邊則是相應(yīng)的監(jiān)控和運(yùn)維。
但是在系統(tǒng)真正“跑起來(lái)”的時(shí)候,我們發(fā)現(xiàn)很難對(duì)其進(jìn)行部署和擴(kuò)容,因此在改造時(shí),我們做重了整個(gè)緩存客戶端 SDK,并引入了場(chǎng)景的配置。
我們通過(guò)進(jìn)行本地緩存的管理,添加過(guò)濾條件,以保證客戶端讀取緩存時(shí),能夠知道具體的數(shù)據(jù)源和基本的協(xié)議,從而判斷出要訪問(wèn)的是 Redis、還是 MemCache、或是其他類型的存儲(chǔ)。
在 Cache 客戶端做好之后,我們又碰到了新的問(wèn)題:由于同程使用了包括 Java、.Net、Go,Node.js 等多種語(yǔ)言的開發(fā)模式,如果為每一種語(yǔ)言都準(zhǔn)備和維護(hù)一套 Cache 的客戶端的話,顯然非常耗費(fèi)人力。
同時(shí),對(duì)于維護(hù)來(lái)說(shuō):只要是程序就會(huì)有 Bug,只要有 Bug 就需要升級(jí)。一旦所有事業(yè)部的所有應(yīng)用都要升級(jí) SDK,那么對(duì)于所有嵌套應(yīng)用的中間件來(lái)說(shuō),都要進(jìn)行升級(jí)測(cè)試,這將會(huì)牽扯到巨大的回歸量。
可以說(shuō)這樣的反復(fù)測(cè)試幾乎是不現(xiàn)實(shí)的。于是我們需要做出一個(gè)代理層,通過(guò)把協(xié)議、過(guò)濾、場(chǎng)景等內(nèi)容下沉到 Proxy 中,以實(shí)現(xiàn)SDK的整體輕量化。
與此同時(shí),我們?cè)诓渴饡r(shí)也引入了容器,將整個(gè) Redis 都運(yùn)行在容器之中,并讓容器去完成整個(gè)應(yīng)用的部署。
通過(guò)容器化的部署,集群的建立變得極其簡(jiǎn)單,我們也大幅豐富了集群的方案。
我們實(shí)現(xiàn)了為每個(gè)應(yīng)用場(chǎng)景都能配有一個(gè)(或一種)Key,并且被一個(gè)(或一種)集群來(lái)服務(wù)。
眾所周知,Redis 雖然實(shí)現(xiàn)了遷移擴(kuò)容,但是其操作較為復(fù)雜。因此我們自行研發(fā)了一套遷移調(diào)度系統(tǒng),自動(dòng)化地實(shí)現(xiàn)了從流量擴(kuò)容到數(shù)據(jù)擴(kuò)容、以及從縱向到橫向的擴(kuò)容。
如前所述,我們有著 Redis 和 Memcache 兩種客戶端,它們是使用不同的協(xié)議進(jìn)行訪問(wèn)。因此,我們通過(guò)統(tǒng)一的 Proxy 來(lái)實(shí)現(xiàn)良好的支持。
如今在我們的緩存平臺(tái)上,運(yùn)維人員唯一需要做的就是:往該緩存平臺(tái)里添加一臺(tái)物理服務(wù)器、插上網(wǎng)線、然后系統(tǒng)就能夠自動(dòng)發(fā)現(xiàn)新的服務(wù)器的加入,進(jìn)而開啟 Redis。
而對(duì)于單場(chǎng)景下的 Redis 實(shí)例,我們也能夠通過(guò)控制臺(tái),以獲取包括 Top10 的 Key、當(dāng)前訪問(wèn)最多的 Key、Key 的屬主、最后由誰(shuí)執(zhí)行了寫入或修改等多個(gè)監(jiān)控項(xiàng)。
可見(jiàn),由于上下層都是自建的,因此我們擴(kuò)展了原來(lái) Redis 里沒(méi)有的監(jiān)控項(xiàng)。
上圖是 Topkey 使用情況的一個(gè)示例,就像程序在故障時(shí)經(jīng)常用到的 Dump 文件一樣,它能夠反映出后續(xù)的各種編排。
王曉波,同程藝龍機(jī)票事業(yè)群 CTO,專注于高并發(fā)互聯(lián)網(wǎng)架構(gòu)設(shè)計(jì)、分布式電子商務(wù)交易平臺(tái)設(shè)計(jì)、大數(shù)據(jù)分析平臺(tái)設(shè)計(jì)、高可用性系統(tǒng)設(shè)計(jì)。 設(shè)計(jì)過(guò)多個(gè)并發(fā)百萬(wàn)以上平臺(tái)。 擁有十多年豐富的技術(shù)架構(gòu)、技術(shù)咨詢經(jīng)驗(yàn),深刻理解電商系統(tǒng)對(duì)技術(shù)選擇的重要性。
【原創(chuàng)稿件,合作站點(diǎn)轉(zhuǎn)載請(qǐng)注明原文作者和出處為.com】
當(dāng)前標(biāo)題:從Memcache轉(zhuǎn)戰(zhàn)Redis,聊聊緩存使用填過(guò)的“坑”
轉(zhuǎn)載源于:http://www.dlmjj.cn/article/coohiie.html


咨詢
建站咨詢
