無論是CDN緩存加速,還是CPU的三級(jí)緩存,又或者是在如今互聯(lián)網(wǎng)時(shí)代流量紅利所帶來的高并發(fā)結(jié)構(gòu)客戶端,而不得不使用緩存架構(gòu)。緩存,對(duì)于技術(shù)人來說,是一個(gè)必須直面的名詞。 然而,如何清晰明了的選擇緩存服務(wù)以及如何在設(shè)計(jì)架構(gòu)時(shí)使用緩存去優(yōu)化業(yè)務(wù),對(duì)于我們很多人來說,一直以來都比較迷惑,本文從這一點(diǎn)出發(fā),簡(jiǎn)單介紹了緩存概念和分布式緩存服務(wù)的一些應(yīng)用場(chǎng)景。 緩存的必要性 一般而言,互聯(lián)網(wǎng)的典型架構(gòu)可以分為三層模式,客戶端層,站點(diǎn)層,數(shù)據(jù)層。而架構(gòu)分層的本質(zhì)是一個(gè)“數(shù)據(jù)移動(dòng)”的過程,然后“被處理”和“被呈現(xiàn)”的過程。用戶請(qǐng)求從界面(瀏覽器或App界面)到網(wǎng)絡(luò)轉(zhuǎn)發(fā)、應(yīng)用服務(wù)再到存儲(chǔ)(數(shù)據(jù)庫或文件系統(tǒng)),然后返回到界面呈現(xiàn)內(nèi)容。 而隨著互聯(lián)網(wǎng)的普及與發(fā)展,伴隨而來的是內(nèi)容信息類型日益復(fù)雜。同時(shí),由于移動(dòng)互聯(lián)網(wǎng)的流量紅利所帶來的用戶數(shù)和訪問量,更是造就了最高10億DAU的“微信神話”。 因此,近幾年爆炸式的互聯(lián)網(wǎng)發(fā)展也后端架構(gòu)提出了新的挑戰(zhàn)——如何去平衡應(yīng)用服務(wù)器和數(shù)據(jù)庫服務(wù)器成本和性能之間的矛盾。 資源往往是有限的,同時(shí),關(guān)系型數(shù)據(jù)庫的讀寫能力也受限于磁盤,每秒能夠接收的請(qǐng)求次數(shù)也是有限的,如何能夠有效利用有限的資源來提供盡可能大的吞吐量? 引入緩存層,是實(shí)現(xiàn)資源的高效利用和降低用戶交互延時(shí)的不二法則。 緩存的影響因素和分類2.1 介質(zhì)因素了解緩存在架構(gòu)設(shè)計(jì)中的應(yīng)用,首先我們來看下緩存的分類。最基礎(chǔ)的如CPU緩存,CPU緩存定義為CPU與內(nèi)存之間的臨時(shí)數(shù)據(jù)交換器,為解決CPU運(yùn)行處理速度與內(nèi)存讀寫速度不匹配的矛盾而誕生,一般直接集成在CPU芯片上,這里就不展開細(xì)講了。另外就是本地緩存和分布式緩存,聊到這兩者時(shí),我們先來了解下存儲(chǔ)介質(zhì)。 從硬件介質(zhì)角度而言,存儲(chǔ)介質(zhì)廣義上可以分為內(nèi)存和硬盤,其中內(nèi)存(RAM)作為“指令中轉(zhuǎn)器”,只負(fù)責(zé)臨時(shí)性存儲(chǔ)。磁盤作為“外存”,可以持久化存儲(chǔ)。 · 內(nèi)存:將緩存存儲(chǔ)于內(nèi)存中是最快的選擇,無需額外的I/O開銷,但是內(nèi)存的缺點(diǎn)是沒有持久化落地物理磁盤,一旦應(yīng)用異常break down而重新啟動(dòng),數(shù)據(jù)很難或者無法復(fù)原。 · 硬盤:一般來說,很多緩存框架會(huì)結(jié)合使用內(nèi)存和硬盤,在內(nèi)存分配空間滿了或是在異常的情況下,可以被動(dòng)或主動(dòng)的將內(nèi)存空間數(shù)據(jù)持久化到硬盤中,達(dá)到釋放空間或備份數(shù)據(jù)的目的。 由于馮諾依曼式自身模型原因,就數(shù)據(jù)傳輸速度而言,CPU緩存 > 內(nèi)存 > 硬盤。 上圖是一個(gè)典型數(shù)據(jù)“被處理”過程,而我們常說的存儲(chǔ),依托于硬盤介質(zhì),而緩存,更多是需要內(nèi)存 + 硬盤結(jié)合。 2.2 緩存分類了解了基本的存儲(chǔ)介質(zhì)知識(shí)后,我們接下來認(rèn)識(shí)緩存分類,根據(jù)應(yīng)用架構(gòu)中的耦合度,分為local cache(本地緩存)和 remote cache(分布式緩存)。 · 本地緩存:也叫進(jìn)程內(nèi)緩存,顧名思義,指應(yīng)用中的緩存組件,優(yōu)點(diǎn)是應(yīng)用和緩存在同一進(jìn)程內(nèi)部,進(jìn)程內(nèi)緩存省去了網(wǎng)絡(luò)開銷,所以一來節(jié)省了內(nèi)網(wǎng)帶寬,二來響應(yīng)時(shí)延會(huì)更低。缺點(diǎn)就是多個(gè)應(yīng)用無法共享緩存,且難以保持進(jìn)程緩存的一致性。 · 分布式緩存:也叫進(jìn)程外緩存,指的是與應(yīng)用分離的緩存組件或服務(wù),其最大的優(yōu)點(diǎn)是自身就是一個(gè)獨(dú)立的應(yīng)用,與本地應(yīng)用隔離,多個(gè)應(yīng)用可直接的共享緩存。如我們常見的memcache和Redis數(shù)據(jù)庫。 而在分層架構(gòu)設(shè)計(jì)中,有一條準(zhǔn)則:即站點(diǎn)層、服務(wù)層需達(dá)到無狀態(tài)無數(shù)據(jù)。 其目的是為了當(dāng)業(yè)務(wù)需要時(shí),能夠任意的增加節(jié)點(diǎn)水平擴(kuò)展。所以數(shù)據(jù)和狀態(tài)盡量存儲(chǔ)到后端的數(shù)據(jù)存儲(chǔ)服務(wù),例如數(shù)據(jù)庫服務(wù)或者緩存服務(wù)。當(dāng)然,如果業(yè)務(wù)處于“極其高并發(fā)且業(yè)務(wù)一定程度允許不一致”的場(chǎng)景,也可以考慮使用本地緩存,其它一般不推薦使用。 主流分布式緩存分析在對(duì)比之前,我們先來了解下分布式緩存數(shù)據(jù)庫在分層架構(gòu)中的位置,這樣有助于我們明確的認(rèn)識(shí)到緩存所起到的作用。 見上圖,按照經(jīng)典互聯(lián)網(wǎng)架構(gòu)三層模式,簡(jiǎn)單畫出了站點(diǎn)層和數(shù)據(jù)層的交互邏輯。加入了緩存服務(wù)后,這里也定義它為緩存服務(wù)層,其處于站點(diǎn)層和數(shù)據(jù)層的中間,同時(shí)依賴于兩者提供雙向的“數(shù)據(jù)移動(dòng)”。既然如此,當(dāng)我們想要加入分布式緩存服務(wù)時(shí),那么圖中緩存服務(wù)層中的Redis和memcache兩者又該如何去選擇呢? 3.1 使用率分析Redis和memcache都是互聯(lián)網(wǎng)分層架構(gòu)中,最常用的KV緩存服務(wù)。盡管memcache首發(fā)(2003年)比Redis首發(fā)(2009年)早的多,兩者也都是使用C語言編寫,但是當(dāng)Redis一經(jīng)發(fā)布,迅速就成為了架構(gòu)師手中設(shè)計(jì)分層架構(gòu)時(shí)的優(yōu)先選擇。 這里只找到一張截止到17年時(shí)的使用率對(duì)比分析,不難看出Redis使用率一直呈現(xiàn)上升趨勢(shì),到目前更是遠(yuǎn)遠(yuǎn)的甩下了memcahce。 3.2 功能分析在對(duì)比前,先來了解Redis和memcahce數(shù)據(jù)庫分別到底是什么以及它們的基本概念。 · Redis:一個(gè)開源的、Key-Value型、基于內(nèi)存運(yùn)行并支持持久化的NoSQL數(shù)據(jù)庫; · memcached:一款完全開源、高性能的、分布式的內(nèi)存系統(tǒng); 關(guān)鍵詞:內(nèi)存、持久化。 其實(shí)關(guān)鍵詞已經(jīng)為我們涵蓋了Redis和memcahce兩者的核心作用。Redis的持久化+緩存,memcache的緩存。如果把兩者比如成學(xué)生,那么“memcache”就像是一名特長生,專項(xiàng)發(fā)展。而“Redis”則是一名三好學(xué)生,“德智育”全面發(fā)展。 接下來我們從不同維度詳細(xì)分析下Redis和memcahce數(shù)據(jù)庫兩者的區(qū)別,以便于大家能夠更好的區(qū)別并選擇適合自己的緩存數(shù)據(jù)庫。 一表勝千言,這是來自“特長生”和“三好學(xué)生”的較量。根據(jù)上圖,下面我們來分析下兩者在什么場(chǎng)景下更加適用。 3.3 應(yīng)用場(chǎng)景分析3.3.1 什么時(shí)候傾向于適用Redis?業(yè)務(wù)需求決定技術(shù)選型,當(dāng)業(yè)務(wù)有這樣一些特點(diǎn)的時(shí)候,選擇Redis會(huì)更加適合。 a 存在復(fù)雜數(shù)據(jù)結(jié)構(gòu)Redis支持5種存儲(chǔ)類型,包含字符串、哈希、列表、集合、有序集合等,而Menmcache只支持KV。 假設(shè)當(dāng)緩存數(shù)據(jù)類型比較復(fù)雜時(shí),推薦使用Redis,這種場(chǎng)景多見于用戶訂單列表,用戶消息,帖子評(píng)論列表等。 b 當(dāng)需要考慮緩存持久化時(shí)Redis支持固化功能,當(dāng)數(shù)據(jù)庫崩潰后重啟,內(nèi)存可以迅速的恢復(fù)熱數(shù)據(jù)。無需主動(dòng)或被動(dòng)的預(yù)熱,減少因Redis瞬間壓力過大導(dǎo)致的后端數(shù)據(jù)庫雪崩風(fēng)險(xiǎn)。 Redis的固化模式分為兩種模式,一種是RDB快照模式,另外一種是AOF持久化模式。兩者的用途不同,請(qǐng)看下圖。 這里需要注意的是,RDB定期快照不能保證萬無一失,且AOF會(huì)降低Redis的效率。 同時(shí),也別看著Redis有持久化功能,就跟打了雞血一樣想省下Mysql數(shù)據(jù)庫的錢,記住,讓專業(yè)的工具做專業(yè)的事情。 ps:如果是云數(shù)據(jù)庫Redis(阿里云、七牛云)是默認(rèn)開啟固化的,所以是內(nèi)存+硬盤形式。 c 當(dāng)需要高可用時(shí)Redis天然支持集群功能,可以實(shí)現(xiàn)主動(dòng)復(fù)制,讀寫分離。Redis在擴(kuò)展和穩(wěn)定高可用性能方面都是比較成熟的。 Redis官方也提供了sentinel集群管理工具,能夠?qū)崿F(xiàn)主從服務(wù)監(jiān)控,故障自動(dòng)轉(zhuǎn)移,最重要的是,這些對(duì)于客戶端都是透明的,無需程序改動(dòng),也無需人工介入。 而Memcache本身并不支持集群,所有的集群形式都是通過客戶端實(shí)現(xiàn)。要想要實(shí)現(xiàn)高可用,需要進(jìn)行二次開發(fā),需要例如客戶端的雙讀雙寫或者服務(wù)端的集群同步等。 如果業(yè)務(wù)當(dāng)有緩存高可用場(chǎng)景需求時(shí),那么使用Redis比memcahce簡(jiǎn)便的多。例如在即時(shí)通訊業(yè)務(wù)中,用戶的在線狀態(tài),就有高可用需求。 d 當(dāng)Vlaue值很大時(shí)前文也說了,Redis和Memcache都是以KV形式存儲(chǔ),那么除了數(shù)據(jù)類型因素,選擇Redis,還有什么因素影響呢? 答案是Value值的大小。 在Redis官網(wǎng)的文檔中,我們可以查閱到,Redis支持多種復(fù)雜數(shù)據(jù)結(jié)構(gòu),也因此,支持Key和Value值大小最大可以到512M。而Memcache的key和Value值大小都被限制在1M以內(nèi)。 所以,當(dāng)我們?nèi)绻衚ey-value值非常大的緩存服務(wù)應(yīng)用場(chǎng)景時(shí),那么也只能使用Redis了。 3.3.2 什么時(shí)候傾向于適用Memcache?說了這么多關(guān)于Redis的好,甚至有種memcahe就是Redis子集的錯(cuò)覺,而memcache有的功能,似乎Redis都有了。非也,作為“特長生”,當(dāng)你面臨以下場(chǎng)景時(shí),那么選擇memcache緩存服務(wù),比Redis可能更好一些。 a 數(shù)據(jù)量大,并發(fā)量大的業(yè)務(wù)這里的前提是緩存數(shù)據(jù)類型支持,即純KV場(chǎng)景。如果業(yè)務(wù)存在數(shù)據(jù)量大,并發(fā)量大的需求,那么使用memcache或許更適合。 這個(gè)也和memcache的底層實(shí)現(xiàn)原理有關(guān)。 如上圖,當(dāng)在內(nèi)存分配、線程模型和網(wǎng)絡(luò)模型維度考慮時(shí),如果當(dāng)你的業(yè)務(wù)符合是數(shù)據(jù)量大,并發(fā)量大的緩存業(yè)務(wù)場(chǎng)景時(shí),使用memcache比redis能達(dá)到訪問更快,同時(shí),延時(shí)更低。這個(gè)時(shí)候,選擇memcache就再恰當(dāng)不過了。 探討4.1 保持緩存一致性的方式前面我們已經(jīng)分析了Redis和memcache的功能對(duì)比以及其衍生出來的場(chǎng)景描述,最后千言萬語不如一句話:業(yè)務(wù)需求決定技術(shù)選型。選擇適合業(yè)務(wù)的緩存服務(wù)最為重要。 既然是緩存服務(wù),我們都知道,用戶訪問到時(shí),站點(diǎn)層先看緩存服務(wù)層是否能hit數(shù)據(jù),如果miss,則會(huì)到后端數(shù)據(jù)庫拿到數(shù)據(jù)再原路返回給用戶,同時(shí)緩存服務(wù)層set。 假設(shè),當(dāng)緩存服務(wù)層存在數(shù)據(jù),但是這時(shí)候,剛好用戶也在發(fā)送寫請(qǐng)求,那么這個(gè)用戶hit,則會(huì)返回舊數(shù)據(jù)。出現(xiàn)這種情況,歸根結(jié)底還是因?yàn)閿?shù)據(jù)庫和緩存主從延時(shí)導(dǎo)致。 如何保持緩存一致性,這是個(gè)值得深思的問題。也引申出了當(dāng)用戶發(fā)出寫請(qǐng)求時(shí),應(yīng)該先寫緩存還是數(shù)據(jù)庫這個(gè)疑問。 Cache Aside Pattern:簡(jiǎn)稱旁路緩存方案?;驹砭褪菙?shù)據(jù)庫有主數(shù)據(jù)庫(用于寫)、從數(shù)據(jù)庫(用于讀),另有緩存用于提升讀寫效率; · 讀請(qǐng)求:標(biāo)準(zhǔn)的用戶訪問模式。站點(diǎn)層-緩存服務(wù)層-數(shù)據(jù)庫層 · 寫請(qǐng)求:先寫主數(shù)據(jù)庫,再淘汰緩存。 而目前,主流如微軟、臉書等公司都是使用都是Cache-Aside pattern(旁路緩存方案),針對(duì)寫請(qǐng)求,即先寫數(shù)據(jù)庫,然后再淘汰緩存。如果先操作緩存,在讀寫并發(fā)時(shí),可能出現(xiàn)數(shù)據(jù)不一致情況(數(shù)據(jù)庫主從未同步中的間隔時(shí)間)。 這種旁路緩存方案,也是為了保障最終數(shù)據(jù)庫是正確的,而對(duì)于緩存的不一致,有限時(shí)間內(nèi)的不一致是允許的(參考CAP原則和Base理論)。當(dāng)然,這里也有一個(gè)隱藏的坑點(diǎn),假設(shè)當(dāng)寫入數(shù)據(jù)庫已經(jīng)成功的,但是之后淘汰緩存失敗了,針對(duì)這種情況,這里也提供一個(gè)簡(jiǎn)單的思路。 流程如下圖所示: (1)更新數(shù)據(jù)庫數(shù)據(jù) (2)數(shù)據(jù)庫會(huì)將操作信息寫入binlog日志當(dāng)中 (3)訂閱程序(DTS或者cannal)提取出所需要的數(shù)據(jù)以及key (4)另起一段非業(yè)務(wù)代碼,獲得該信息 (5)嘗試刪除緩存操作,發(fā)現(xiàn)刪除失敗 (6)將這些信息發(fā)送至消息隊(duì)列 (7)重新從消息隊(duì)列中獲得該數(shù)據(jù),重試操作。 4.2 使用緩存服務(wù)的幾點(diǎn)誤區(qū)a 使用緩存,不考慮雪崩我們先來認(rèn)識(shí)下什么是緩存雪崩。 · 緩存雪崩:當(dāng)緩存服務(wù)器重啟或者大量緩存集中在某一個(gè)時(shí)間段失效,這樣在失效的時(shí)間段內(nèi),站點(diǎn)層會(huì)給后端系統(tǒng)(比如DB)帶來很大壓力。甚至直接壓垮數(shù)據(jù)庫,直接導(dǎo)致系統(tǒng)整體不可用。一般來說,在分層架構(gòu)中,緩存服務(wù)最高能幫數(shù)據(jù)庫層抗住90%的壓力,如果當(dāng)緩存數(shù)據(jù)庫出現(xiàn)崩潰時(shí),如果事先未做好規(guī)劃,將直接導(dǎo)致雪崩。 為了預(yù)防上述情況,首先要做好容量預(yù)估,同時(shí),使用采用高可用緩存集群,最好災(zāi)備方案,當(dāng)一個(gè)緩存服務(wù)器服務(wù)掛掉時(shí),能夠做到自動(dòng)切換服務(wù)。 ps:這也是為啥云數(shù)據(jù)庫受歡迎的原因,簡(jiǎn)單,省心。 b 將緩存服務(wù)層當(dāng)做傳遞數(shù)據(jù)媒介簡(jiǎn)單來說,將緩存服務(wù)層當(dāng)做MQ(消息隊(duì)列)使用,通過緩存?zhèn)鬟f數(shù)據(jù),從而實(shí)現(xiàn)兩個(gè)服務(wù)通信的目的,如下圖。 先不說專業(yè)工具做專業(yè)的事情,就一點(diǎn),如果使用緩存?zhèn)鬟f數(shù)據(jù)的話,會(huì)直接導(dǎo)致服務(wù)耦合。 而MQ,作為互聯(lián)網(wǎng)架構(gòu)解耦神器,天然支持集群高可用,而且支持?jǐn)?shù)據(jù)落存儲(chǔ)。 ps:使用MQ后,上游不知道彼此存在,也不需要關(guān)注哪些下游訂閱了消息,這樣直接達(dá)到服務(wù)解耦的效果。 參考文獻(xiàn)1、緩存那些事---美團(tuán)技術(shù)團(tuán)隊(duì) 2、緩存架構(gòu)設(shè)計(jì),從此不再發(fā)愁---58沈劍 3、分布式之?dāng)?shù)據(jù)庫和緩存雙寫一致性方案解析--孤獨(dú)煙 【End】 熱 文 推 薦 ?京東強(qiáng)推 995 工作制,中國式變態(tài)加班何時(shí)休? ?中國程序員在德國:海外版抖音火爆,IT 人才稀缺!| 暢言 ?大數(shù)據(jù)背后的無奈與焦慮:“128元連衣裙”劃分矮窮挫與白富美? ?身為程序員的父母,你年薪多少才能讓“碼二代” 不輸起跑線上? ?20-75K | 互聯(lián)網(wǎng)寒冬中,架構(gòu)師、工程師、產(chǎn)品經(jīng)理、運(yùn)營仍為招聘剛需, 趕緊來投吧! ?再不編程就老了!05 后比特幣專家準(zhǔn)備賺個(gè) 134,000,000 元! System.out.println('點(diǎn)個(gè)好看吧!'); |
|