2021-03-17 分類: 網(wǎng)站建設(shè)
在一個炎熱的夏天,引爆了埋藏已久的大炸彈。本文作者從實際案例出發(fā)講解 Redis 使用的誤區(qū)。
1案例一:一個產(chǎn)品線開發(fā)人員搭建起了一套龐大的價格存儲系統(tǒng),底層是關(guān)系型數(shù)據(jù)庫,只用來處理一些事務(wù)性的操作和存放一些基礎(chǔ)數(shù)據(jù)。
在關(guān)系型數(shù)據(jù)庫的上面還有一套 MongoDB,因為 MongoDB 的文檔型數(shù)據(jù)結(jié)構(gòu),讓他們用起來很順手,同時也可以支撐一定量的并發(fā)。
在大部分的情況下,一次大數(shù)據(jù)量的計算后結(jié)果可以重用但會出現(xiàn)細節(jié)數(shù)據(jù)的頻繁更新,所以他們又在 MongoDB 上搭建了一層 Redis 的緩存。
這樣就形成了數(shù)據(jù)庫→MongoDB→Redis三級的方式,方案本身先不評價不是本文重點,我們來看 Redis 這層的情況。
由于數(shù)據(jù)量巨大,所以需要 200GB 的 Redis,并且在真實的調(diào)用過程中,Redis 是請求量大的點。
當然如果 Redis 有故障時,也會有備用方案,從后面的 MongoDB 和數(shù)據(jù)庫中重新加載數(shù)據(jù)到 Redis,就是這么一套簡單的方案上線了。
當這個系統(tǒng)剛開始運行的時候,一切都還安好,只是運維同學(xué)有點傻眼了, 200GB 的 Redis 單服務(wù)器去做,它的故障可能性太大了。
所以大家建議將它分片,沒分不知道,一分嚇一跳,各種類型用的太多了,特別是里面還有一些類似消息隊列使用的場景。
由于開發(fā)同學(xué)對 Redis 使用的注意點關(guān)注不夠,一味的濫用,一錘了事,所以讓事情變的困難了。
有些僥幸不死的想法是會傳染,這時的每個人都心存僥幸,懶惰心理,都想著:“這個應(yīng)該沒事,以后再說吧,先做個主從,掛了就起從”,這種僥幸也是對 Redis 的虛偽的信心,無知者無畏。
可惜事情往往就是怕什么來什么,在大家快樂的放肆使用時,系統(tǒng)中重要的節(jié)點 MongoDB 由于系統(tǒng)內(nèi)核版本的 Bug,造成整個 MongoDB 集群掛了?。ㄟ@里不多說 MongoDB 的事情,這也是一個好玩的哭器)。
當然,這個對天天與故障為朋友的運維同學(xué)來說并沒什么,對整個系統(tǒng)來說問題也不大,因為大部分請求調(diào)用都是在最上層的 Redis 中完成的,只要做一定降級就行,等拉起了 MongoDB 集群后自然就會好了。
但此時可別忘了那個 Redis,是一個 200G 大的 Redis,更是帶了個從機的 Redis。
所以這時的 Redis 是絕對不能出任何問題的,一旦有故障,所有請求會立即全部打向最低層的關(guān)系型數(shù)據(jù)庫,在如此大量的壓力下,數(shù)據(jù)庫瞬間就會癱瘓。
但是,怕什么來什么,還是出了狀況:主從 Redis 之間的網(wǎng)絡(luò)出現(xiàn)了一點小動蕩,想想這么大的一個東西在主從同步,一旦網(wǎng)絡(luò)動蕩了一下下,會怎么樣呢?
主從同步失敗,同步失敗,就直接開啟全同步,于是 200GB 的 Redis 瞬間開始全同步,網(wǎng)卡瞬間打滿。
為了保證 Redis 能夠繼續(xù)提供服務(wù),運維同學(xué)直接關(guān)掉從機,主從同步不存在了,流量也恢復(fù)正常。不過,主從的備份架構(gòu)變成了單機 Redis,心還是懸著的。
俗話說,福無雙至,禍不單行。這 Redis 由于下層降級的原因并發(fā)操作量每秒增加到四萬多,AOF 和 RDB 庫明顯扛不住。
同樣為了保證能持續(xù)地提供服務(wù),運維同學(xué)也關(guān)掉了 AOF 和 RDB 的數(shù)據(jù)持久化,連最后的保護也沒有了(其實這個保護本來也沒用,200GB 的 Redis 恢復(fù)太大了)。
至此,這個 Redis 變成了完全的單機內(nèi)存型,除了祈禱它不要掛,已經(jīng)沒有任何方法了。
這事懸著好久,直到修復(fù) MongoDB 集群才了事。如此的僥幸,沒出大事,但心里會踏實嗎?回答是不會。
在這個案例中主要的問題在于:對 Redis 過度依賴,Redis 看似為系統(tǒng)帶來了簡單又方便的性能提升和穩(wěn)定性,但在使用中缺乏對不同場景的數(shù)據(jù)的分離造成了一個邏輯上的單點問題。
當然這問題我們可以通過更合理的應(yīng)用架構(gòu)設(shè)計來解決,但是這樣解決不夠優(yōu)雅也不夠徹底,也增加了應(yīng)用層的架構(gòu)設(shè)計的麻煩。
Redis 的問題就應(yīng)該在基礎(chǔ)緩存層來解決,這樣即使還有類似的情況也沒有問題。
因為基礎(chǔ)緩存層已經(jīng)能適應(yīng)這樣的用法,也會讓應(yīng)用層的設(shè)計更為簡單(簡單一直是架構(gòu)設(shè)計所追求的,Redis 的大量隨意使用本身就是追求簡單的副產(chǎn)品,那我們?yōu)槭裁床蛔屵@簡單變?yōu)檎鎸嵞???/p>
2案例二:我們再來看第二個案例,有個部門用自己現(xiàn)有 Redis 服務(wù)器做了一套日志系統(tǒng),將日志數(shù)據(jù)先存儲到 Redis 里面,再通過其他程序讀取數(shù)據(jù)并進行分析和計算,用來做數(shù)據(jù)報表。
當他們做完這個項目之后,這個日志組件讓他們覺得用的還很過癮。他們都覺得這個做法不錯,可以輕松地記錄日志,分析起來也挺快,還用什么公司的分布式日志服務(wù)啊?
隨著時間的流逝,這個 Redis 上已經(jīng)悄悄地掛載了數(shù)千個客戶端,每秒的并發(fā)量數(shù)萬,系統(tǒng)的單核 CPU 使用率也接近 90% 了,此時這個 Redis 已經(jīng)開始不堪重負。
終于,壓死駱駝的最后一根稻草來了,有程序向這個日志組件寫入了一條 7MB 的日志(哈哈,這個容量可以寫一部小說了,這是什么日志啊)。
于是 Redis 堵死了,一旦堵死,數(shù)千個客戶端就全部無法連接,所有日志記錄的操作全部失敗。
其實日志記錄失敗本身應(yīng)該不至于影響正常業(yè)務(wù),但是由于這個日志服務(wù)不是公司標準的分布式日志服務(wù),所以關(guān)注的人很少。
最開始寫它的開發(fā)同學(xué)也不知道會有這么大的使用量,運維同學(xué)更不知有這個非法的日志服務(wù)存在。
這個服務(wù)本身也沒有很好地設(shè)計容錯,所以在日志記錄的地方就直接拋出異常,結(jié)果全公司相當一部分的業(yè)務(wù)系統(tǒng)都出現(xiàn)了故障,監(jiān)控系統(tǒng)中“5XX”的錯誤直線上升。
一幫人欲哭無淚,頂著巨大的壓力排查問題,但是由于受災(zāi)面實在太廣,排障的壓力是可以想像的。
這個案例中看似是一個日志服務(wù)沒做好或者是開發(fā)流程管理不到位,而且很多日志服務(wù)也都用到了 Redis 做收集數(shù)據(jù)的緩沖,好像也沒什么問題。
其實不然,像這樣大規(guī)模大流量的日志系統(tǒng)從收集到分析要細細考慮的技術(shù)點是巨大的,而不只是簡單的寫入性能的問題。
在這個案例中 Redis 給程序帶來的是超簡單的性能解決方案,但這個簡單是相對的,它是有場景限制的。
在這里,這樣的簡單就是毒藥,無知的吃下是要害死自己的,這就像“一條在小河溝里無所不能傲慢的小魚,那是因為它沒見過大海,等到了大?!?。
在這個案例的另一問題:一個非法日志服務(wù)的存在,表面上是管理問題,實質(zhì)上還是技術(shù)問題。
因為 Redis 的使用無法像關(guān)系型數(shù)據(jù)庫那樣有 DBA 的監(jiān)管,它的運維者無法管理和提前知道里面放的是什么數(shù)據(jù),開發(fā)者也無需任何申明就可以向 Redis 中寫入數(shù)據(jù)并使用。
所以這里我們發(fā)現(xiàn) Redis 的使用沒這些場景的管理后在長期的使用中比較容易失控,我們需要一個對 Redis 使用可治理和管控的透明層。
兩個小例子中看到在 Redis 亂用的那個年代里,使用它的兄弟們一定是痛的,承受了各種故障的狂轟濫炸:
如何改變 Redis 用不好的誤區(qū)?
這樣的亂象一定是不可能繼續(xù)了,最少在同程,這樣的使用方式不可以再繼續(xù)了,使用者也開始從喜歡到痛苦了。
怎么辦?這是一個很沉重的事情:“一個被人用亂的系統(tǒng)就像一桌燒壞的菜,讓你重新回爐,還讓人叫好,是很困難的”。
關(guān)鍵是已經(jīng)用的這樣了,總不可能讓所有系統(tǒng)都停下來,等待新系統(tǒng)上線并瞬間切換好吧?這是個什么活:“高速公路上換輪胎”。
但問題出現(xiàn)了總是要解決的,想了再想,論了再論,總結(jié)以下幾點:
留給開發(fā)同學(xué)的時間并不多,只有兩個月的時間來完成這些事情。這事還是很有挑戰(zhàn)的,考驗開發(fā)同學(xué)這個輪胎到底能不換下來的時候到來了。
同學(xué)們開始研發(fā)我們自己的 Redis 緩存系統(tǒng),下面我們來看一下這個代號為鳳凰的緩存系統(tǒng)第一版方案:
首先是監(jiān)控系統(tǒng)。原有的開源 Redis 監(jiān)控從大面上講只是一些監(jiān)控工具,不能算作一個完整的監(jiān)控系統(tǒng)。當然這個監(jiān)控是全方位從客戶端開始一直到返回數(shù)據(jù)的全鏈路的監(jiān)控。
其次是改造 Redis 客戶端。廣泛使用的 Redis 客戶端有的太簡單,有的太重,總之不是我們想要的東西。
比如 .Net 下的 BookSleeve 和 servicestack.Redis(同程還有一點老的 .Net 開發(fā)的應(yīng)用),前者已經(jīng)好久沒人維護了,后者直接收費了。
好吧,我們就開發(fā)一個客戶端,然后督促全公司的研發(fā)用它來替換目前正在使用的客戶端。
在這個客戶端里面,我們植入了日志記錄,記錄了代碼對 Redis 的所有操作事件,例如耗時、Key、Value 大小、網(wǎng)絡(luò)斷開等。
我們將這些有問題的事件在后臺進行收集,由一個收集程序進行分析和處理,同時取消了直接的 IP 端口連接方式,通過一個配置中心分配 IP 地址和端口。
當 Redis 發(fā)生問題并需要切換時,直接在配置中心修改,由配置中心推送新的配置到客戶端,這樣就免去了 Redis 切換時需要業(yè)務(wù)員修改配置文件的麻煩。
另外,把 Redis 的命令操作分拆成兩部分:
這樣就解決了研發(fā)人員使用 Redis 時的規(guī)范問題,并且將 Redis 定位為緩存角色,除非有特殊需求,否則一律以緩存角色對待。
最后,對 Redis 的部署方式也進行了修改,以前是 Keepalived 的方式,現(xiàn)在換成了主從+哨兵的模式。
另外,我們自己實現(xiàn)了 Redis 的分片,如果業(yè)務(wù)需要申請大容量的 Redis 數(shù)據(jù)庫,就會把 Redis 拆分成多片,通過 Hash 算法均衡每片的大小,這樣的分片對應(yīng)用層也是無感知的。
當然重客戶端方式不好,并且我們要做的是緩存,不僅僅是單單的 Redis,于是我們會做一個 Redis 的 Proxy,提供統(tǒng)一的入口點。
Proxy 可以多份部署,客戶端無論連接的是哪個 Proxy,都能取得完整的集群數(shù)據(jù),這樣就基本完成了按場景選擇不同的部署方式的問題。
這樣的一個 Proxy 也解決了多種開發(fā)語言的問題,例如,運維系統(tǒng)是使用 Python 開發(fā)的,也需要用到 Redis,就可以直接連 Proxy,然后接入到統(tǒng)一的 Redis 體系中來。
做客戶端也好,做 Proxy 也好,不只是為代理請求而是為了統(tǒng)一的治理 Redis 緩存的使用,不讓亂象出現(xiàn)。
讓緩存在一個可管可控的場景下穩(wěn)定的運維,讓開發(fā)者可以安全并肆無忌憚繼續(xù)亂用 Redis,但這個“亂”是被虛擬化的亂,因為它的底層是可以治理的。
系統(tǒng)架構(gòu)圖
當然以上這些改造都需要在不影響業(yè)務(wù)的情況下進行。實現(xiàn)這個還是有不小的挑戰(zhàn),特別是分片。
將一個 Redis 拆分成多個,還能讓客戶端正確找到所需要的 Key,這需要非常小心,因為稍有不慎,內(nèi)存的數(shù)據(jù)就全部消失了。
在這段時間里,我們開發(fā)了多種同步工具,幾乎把 Redis 的主從協(xié)議整個實現(xiàn)了一遍,終于可以將 Redis 平滑過渡到新的模式上了。
網(wǎng)頁名稱:如何改變Redis用不好的誤區(qū)?
本文網(wǎng)址:http://muchs.cn/news36/105336.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站維護、網(wǎng)站內(nèi)鏈、商城網(wǎng)站、全網(wǎng)營銷推廣、App開發(fā)、網(wǎng)站收錄
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)
猜你還喜歡下面的內(nèi)容