什么是多級緩存
所謂多級緩存振诬,即在整個系統(tǒng)架構(gòu)的不同系統(tǒng)層級進(jìn)行數(shù)據(jù)緩存蹭睡,以提升訪問效率衍菱,這也是應(yīng)用最廣的方案之一赶么。我們應(yīng)用的整體架構(gòu)如圖1所示:
圖1 多級緩存方案
文末有福利贈送辫呻,如果你感興趣的話可以去領(lǐng)取架構(gòu)資料嘉汰。
整體流程如上圖所示:
1)首先接入Nginx將請求負(fù)載均衡到應(yīng)用Nginx谱净,此處常用的負(fù)載均衡算法是輪詢或者一致性哈希,輪詢可以使服務(wù)器的請求更加均衡颅拦,而一致性哈下瓶玻可以提升應(yīng)用Nginx的緩存命中率怖侦,相對于輪詢,一致性哈希會存在單機(jī)熱點(diǎn)問題谜叹,一種解決辦法是熱點(diǎn)直接推送到接入層Nginx匾寝,一種辦法是設(shè)置一個閥值,當(dāng)超過閥值荷腊,改為輪詢算法艳悔。
2)接著應(yīng)用Nginx讀取本地緩存(本地緩存可以使用Lua Shared Dict、Nginx Proxy Cache(磁盤/內(nèi)存)女仰、Local Redis實(shí)現(xiàn))猜年,如果本地緩存命中則直接返回抡锈,使用應(yīng)用Nginx本地緩存可以提升整體的吞吐量,降低后端的壓力乔外,尤其應(yīng)對熱點(diǎn)問題非常有效床三。
3)如果Nginx本地緩存沒命中,則會讀取相應(yīng)的分布式緩存(如Redis緩存杨幼,另外可以考慮使用主從架構(gòu)來提升性能和吞吐量)勿璃,如果分布式緩存命中則直接返回相應(yīng)數(shù)據(jù)(并回寫到Nginx本地緩存)。
4)如果分布式緩存也沒有命中推汽,則會回源到Tomcat集群补疑,在回源到Tomcat集群時也可以使用輪詢和一致性哈希作為負(fù)載均衡算法。
5)在Tomcat應(yīng)用中歹撒,首先讀取本地堆緩存莲组,如果有則直接返回(并會寫到主Redis集群),為什么要加一層本地堆緩存將在緩存崩潰與快速修復(fù)部分細(xì)聊暖夭。
6)作為可選部分锹杈,如果步驟4沒有命中可以再嘗試一次讀主Redis集群操作。目的是防止當(dāng)從有問題時的流量沖擊迈着。
7)如果所有緩存都沒有命中只能查詢DB或相關(guān)服務(wù)獲取相關(guān)數(shù)據(jù)并返回竭望。
8)步驟7返回的數(shù)據(jù)異步寫到主Redis集群,此處可能多個Tomcat實(shí)例同時寫主Redis集群裕菠,可能造成數(shù)據(jù)錯亂咬清,如何解決該問題將在更新緩存與原子性部分細(xì)聊。
應(yīng)用整體分了三部分緩存:應(yīng)用Nginx本地緩存奴潘、分布式緩存旧烧、Tomcat堆緩存,每一層緩存都用來解決相關(guān)的問題画髓,如應(yīng)用Nginx本地緩存用來解決熱點(diǎn)緩存問題掘剪,分布式緩存用來減少訪問回源率、Tomcat堆緩存用于防止相關(guān)緩存失效/崩潰之后的沖擊奈虾。
雖然就是加緩存夺谁,但是怎么加,怎么用細(xì)想下來還是有很多問題需要權(quán)衡和考量的肉微,接下來部分我們就詳細(xì)來討論一些緩存相關(guān)的問題匾鸥。
如何緩存數(shù)據(jù)
接下來部將從緩存過期、維度化緩存浪册、增量緩存扫腺、大Value緩存、熱點(diǎn)緩存幾個方面來詳細(xì)介紹如何緩存數(shù)據(jù)村象。
過期與不過期
對于緩存的數(shù)據(jù)我們可以考慮不過期緩存和帶過期時間緩存笆环,什么場景應(yīng)該選擇哪種模式需要根據(jù)業(yè)務(wù)和數(shù)據(jù)量等因素來決定攒至。
不過期緩存場景一般思路如圖2所示:
圖2不過期緩存方案
使用Cache-Aside模式迫吐,首先寫數(shù)據(jù)庫,如果成功账忘,則寫緩存志膀。這種場景下存在事務(wù)成功、緩存寫失敗但無法回滾事務(wù)的情況鳖擒。另外溉浙,不要把寫緩存放在事務(wù)中,尤其寫分布式緩存蒋荚,因?yàn)榫W(wǎng)絡(luò)抖動可能導(dǎo)致寫緩存響應(yīng)時間很慢戳稽,引起數(shù)據(jù)庫事務(wù)阻塞。如果對緩存數(shù)據(jù)一致性要求不是那么高期升,數(shù)據(jù)量也不是很大惊奇,則可以考慮定期全量同步緩存。
也有提到如下思路:先刪緩存播赁,然后執(zhí)行數(shù)據(jù)庫事務(wù)颂郎;不過這種操作對于如商品這種查詢非常頻繁的業(yè)務(wù)不適用,因?yàn)樵谀銊h緩存的同時容为,已經(jīng)有另一個系統(tǒng)來讀緩存了乓序,此時事務(wù)還沒有提交。當(dāng)然對于如用戶維度的業(yè)務(wù)是可以考慮的舟奠。
不過為了更好地解決以上多個事務(wù)的問題竭缝,可以考慮使用訂閱數(shù)據(jù)庫日志的架構(gòu),如使用canal訂閱mysql的binlog實(shí)現(xiàn)緩存同步沼瘫。
對于長尾訪問的數(shù)據(jù)、大多數(shù)數(shù)據(jù)訪問頻率都很高的場景咙俩、緩存空間足夠都可以考慮不過期緩存耿戚,比如用戶、分類阿趁、商品膜蛔、價格、訂單等脖阵,當(dāng)緩存滿了可以考慮LRU機(jī)制驅(qū)逐老的緩存數(shù)據(jù)皂股。
1. 過期緩存機(jī)制
即采用懶加載,一般用于緩存別的系統(tǒng)的數(shù)據(jù)(無法訂閱變更消息命黔、或者成本很高)呜呐、緩存空間有限就斤、低頻熱點(diǎn)緩存等場景;常見步驟是:首先讀取緩存如果不命中則查詢數(shù)據(jù)蘑辑,然后異步寫入緩存并過期緩存洋机,設(shè)置過期時間,下次讀取將命中緩存洋魂。熱點(diǎn)數(shù)據(jù)經(jīng)常使用即在應(yīng)用系統(tǒng)上緩存比較短的時間绷旗。這種緩存可能存在一段時間的數(shù)據(jù)不一致情況,需要根據(jù)場景來決定如何設(shè)置過期時間副砍。如庫存數(shù)據(jù)可以在前端應(yīng)用上緩存幾秒鐘衔肢,短時間的不一致時可以忍受的。
2. 維度化緩存與增量緩存
對于電商系統(tǒng)豁翎,一個商品可能拆成如基礎(chǔ)屬性膀懈、圖片列表、上下架谨垃、規(guī)格參數(shù)启搂、商品介紹等;如果商品變更了要把這些數(shù)據(jù)都更新一遍那么整個更新成本很高:接口調(diào)用量和帶寬刘陶;因此最好將數(shù)據(jù)進(jìn)行維度化并增量更新(只更新變的部分)胳赌。尤其如上下架這種只是一個狀態(tài)變更,但是每天頻繁調(diào)用的匙隔,維度化后能減少服務(wù)很大的壓力疑苫。
圖3 維度化緩存方案
按照不同維度接收MQ進(jìn)行更新捍掺。
3. 大Value 緩存
要警惕緩存中的大Value,尤其是使用Redis時再膳。遇到這種情況時可以考慮使用多線程實(shí)現(xiàn)的緩存挺勿,如Memcached,來緩存大Value喂柒;或者對Value進(jìn)行壓縮不瓶;或者將Value拆分為多個小Value,客戶端再進(jìn)行查詢灾杰、聚合蚊丐。
4. 熱點(diǎn)緩存
對于那些訪問非常頻繁的熱點(diǎn)緩存,如果每次都去遠(yuǎn)程緩存系統(tǒng)中獲取艳吠,可能會因?yàn)樵L問量太大導(dǎo)致遠(yuǎn)程緩存系統(tǒng)請求過多麦备、負(fù)載過高或者帶寬過高等問題,最終可能導(dǎo)致緩存響應(yīng)慢,使客戶端請求超時凛篙。一種解決方案是通過掛更多的從緩存黍匾,客戶端通過負(fù)載均衡機(jī)制讀取從緩存系統(tǒng)數(shù)據(jù)。不過也可以在客戶端所在的應(yīng)用/ 代理層本地存儲一份鞋诗,從而避免訪問遠(yuǎn)程緩存膀捷,即使像庫存這種數(shù)據(jù),在有些應(yīng)用系統(tǒng)中也可以進(jìn)行幾秒鐘的本地緩存削彬,從而降低遠(yuǎn)程系統(tǒng)的壓力全庸。
架構(gòu)視頻學(xué)習(xí)
分享一些架構(gòu)學(xué)習(xí)的視頻資料,想要的可以自己去領(lǐng)取融痛。
當(dāng)然還有更多雁刷,這邊就不一一列舉了覆劈,你如果覺得你能全部吃下,也不擋著你要到更多沛励。
視頻獲取方式:【點(diǎn)擊鏈接免費(fèi)領(lǐng)取】https://shimo.im/docs/QzafqtfKU4MoODI9/read
最后的話
為什么某些人會一直比你優(yōu)秀责语,是因?yàn)樗旧砭秃軆?yōu)秀還一直在持續(xù)努力變得更優(yōu)秀。而你是不是還在滿足于現(xiàn)狀且內(nèi)心在竊喜?“對于程序員來說目派,如果哪一天開始他停止了學(xué)習(xí)坤候,那么他的職業(yè)生涯便開始宣告消亡∑蟛洌”所以行動起來白筹,學(xué)習(xí)起來!