鯨品堂|這些年,使用緩存踩過的坑

2022-05-19 528

緩存技術作為提升係統性能的關鍵技術,能很好解(jiě)決高並發係統高QPS、低RT的性能訴求。如何(hé)合理使用這一(yī)技術,是係統設(shè)計研發人員(yuán)經常思考的問題。


緩(huǎn)存(cún)普遍應用在眾(zhòng)多的產品之中(zhōng),從簡單(dān)的使用分布式緩存,到後麵演進使用多級緩存。根據不同(tóng)場景應用(yòng)需(xū)求而不(bú)斷調整架構,為性能提(tí)升提(tí)供了強有力的支撐,但是也因此(cǐ)使得業務係統架構(gòu)日趨複雜,導致係統在穩定性(xìng)方麵也麵臨(lín)了一些挑戰。


本文結合這些年在(zài)緩存使用過程中的一些(xiē)經驗教訓,重點(diǎn)著力於常用場景和常見問題,以及對應的處理(lǐ)思路,前人之坑,後事之(zhī)橋。


分享線索:為什麽要使用緩存 -> 哪些場景適用(yòng),能帶來什麽好處 -> 坑人呐,怎麽辦 -> 例行總結,語重心長





緩存引入




架構設計很重要的一部分內容便是(shì)如何(hé)滿足業務的(de)性能訴求,在性能優化上,利用(yòng)緩存的案例非常多,其本質都是為了彌補內存高讀寫與(yǔ)磁盤慢讀寫之(zhī)間的(de)鴻溝。


一(yī)個(gè)係統的長(zhǎng)期建設,所采用的架(jià)構(gòu)肯定不是一成不變的,會隨著業務不斷變化(huà)而進行對應調整(zhěng),以下(xià)便是在係統建設的不同階段逐步引入不同級別緩存的過程。



1.0時代,業務量(liàng)小,應(yīng)用直接通過數據庫進(jìn)行數據讀寫。

圖片關鍵詞


圖片關鍵(jiàn)詞2.0時代,業務量(liàng)有(yǒu)了一定的增長,數據庫出現性能瓶頸(jǐng),使用分布式緩(huǎn)存進行熱點數據的訪問加速。

圖片關鍵(jiàn)詞


圖片關鍵詞3.0時代,業務量開始暴增,更高頻的熱點數據訪問(wèn),因為網絡io、序列化操作等帶來了性能壓力,采用本地緩存再次進行加速,減少網絡(luò)請求同時(shí)還省去了序列化的開銷。

圖片關(guān)鍵詞

目前分布式緩(huǎn)存、本地緩存相關技術棧(zhàn)都非常多,分(fèn)布式緩存成熟的(de)有redis、memcached等,目(mù)前使用最廣泛的還是redis,本地緩存常用的有(yǒu)ConcurrentHashMap、Guava、caffeine、ehcache、spring cache等,其中spring cache因(yīn)為整合簡單,支持緩存組件廣,使用方(fāng)便,使用越來越廣(guǎng)泛(fàn)。


從具體緩存實現(xiàn)來看,絕大部分情況都是采用旁路緩存,通過應用程序更新緩存,緩存組件不直接操作數(shù)據源。





常用場景




1

會話共享


分(fèn)布式架構情況下,多(duō)個微服務節點(diǎn),必須通過會話共享,才能保證一次登錄,在(zài)分發請求(qiú)後每個服務節點的登錄狀態一致,所以引入分布式緩存進行會話共享。

圖片關鍵詞(cí)


2

數據訪問加速


  • 分布式緩存

    在(zài)高並發訪(fǎng)問數據庫讀多寫少時,為了抗住頻(pín)繁查詢而對數(shù)據庫造成壓力,將數據緩存起來,當有請(qǐng)求過來,直接返回數據。

    同(tóng)時也可以將一些過程數據的頻繁變更基於緩存執行(háng),在過(guò)程完成以後再持久化到數據庫(kù),能有效降低(dī)數據庫訪問壓力(lì),並能帶來數據操作性能的提升。

    典型應用場景:購物車、訂單過程數據等。

  • 本地緩存

    本地緩存規避了網絡及(jí)序列化開銷,本地緩(huǎn)存使用JVM內存和直(zhí)接內存。在容器化部署情況(kuàng)下,內存limit的設置,使緩存空間有限,所(suǒ)以不能進行大數據量的存儲,比較適合熱點隻讀數(shù)據。

    典(diǎn)型應用場景:字(zì)典數據、熱賣產銷品。


3

計數


業務應用上經常要控(kòng)製在(zài)一定時間內對某個數據對象的(de)操作次數,在分布式應(yīng)用情況下,對同一數據對象計數很容易(yì)產生重複計算,數量失控的情況,而使用分布式計數器功能特性可以很好規避。

典型應用場景:防(fáng)止刷單、限製登錄次數、活(huó)動限額等。


4

分布式鎖


本地鎖隻能(néng)鎖住當(dāng)前進程,已經無法滿足當前的係統設計需求(qiú)。分布式鎖支撐同時去一個(gè)地方“鎖占”,如果占到,就執行邏(luó)輯。否則就必須(xū)等待,直到釋放鎖,等待可以自旋的方式。

典型應用場景:在購物車或者提交訂單情況下,係統(tǒng)如(rú)何防止重複提(tí)交。





常見問題




1

緩(huǎn)存一致(zhì)性問題

緩存的一致性就是指緩存中的數據是否(fǒu)和目標存儲中的數據是一樣的,也(yě)就是說緩存中已經修改的(de)數據是否已經保存到了物理(lǐ)存儲中,物理存儲中已經被修的內容,是否與緩存的內容是一樣的。在多級緩存情況下,物理存儲與多級緩存之間的內容也需保持(chí)一樣。


案例:

項目上經常聽見(jiàn)這類聲音,緩存數據與數據庫數據不一致,緩存刷不成功,部分節點數據不一致等等,案例非(fēi)常多,比如:


某(mǒu)集團項目經常出現(xiàn)銷售(shòu)品屬性、產品屬性緩(huǎn)存(cún)與數據(jù)庫不一致的問題,最終確定是因為刷新緩存(cún)讀取的數據源是外部接口,外(wài)部接口偶爾失(shī)敗,導(dǎo)致(zhì)取到結果為空,將空對(duì)象寫入了(le)緩存(cún)中。


某省份項目經常出(chū)現通過清空緩存刷新時,一部分節(jiē)點沒(méi)有執行(háng)成功,後麵定位到是本地緩存刷新線程一定概率發(fā)生異常終止導致,程序沒有捕(bǔ)獲異常,導致清空失敗,沒有加載到最新數據。


某項目使用zk廣播的方式(shì)刷新本地緩存,由於應用FGC很頻繁,刷新緩存時部分節點一定概率出現FGC,導致zk通知失敗,沒有(yǒu)進行結果處理並重試,造成節點間本地緩存不一致。


解決方案:

旁路緩(huǎn)存模式(Cache Aside Pattern)問題分析問(wèn)題前(qián),我們先了解下該模式。

寫(Write)

        圖片關鍵詞           

讀(Read)

圖片關鍵詞


以上模式(shì)基本可(kě)以解決絕大部分場景的使用情況,但是在更新緩存時,因為數據庫操作(zuò)效率肯(kěn)定比緩存操作效率慢,比如更新數(shù)據的查詢語句性能較差,或者並發(fā)情況下出現A線程獲取數據後寫入緩存,B線程同(tóng)時在更新數據並刪除緩存(cún),若B線程完成時間早於(yú)A線程,那麽最終緩存將會是A線程讀取的舊數據(jù)。這種情(qíng)況下,為了保證強一致(zhì)性,可以采用延(yán)遲雙刪,刪除緩(huǎn)存線程執行完以後,再增加一個刪除命令,等待一定時間進行二次刪除。這(zhè)樣(yàng)會增加複雜度,具體要看業務容(róng)忍度。


  • 本地緩存(cún)與分布式緩存不一致(zhì)問題

    分布式緩存一般(bān)隻有一個數據源(yuán),所以一致性容易保證,但是(shì)本地緩存分散到各個應用節(jiē)點中,在更新分布式緩存(cún)同時,如何保證所有(yǒu)應用節點(diǎn)本地(dì)緩存都(dōu)能更新(xīn),一般(bān)有如下方案:

    zk廣(guǎng)播通知,事務執行節點在刷新分布式緩存以後,發起一(yī)條通(tōng)知通過zk廣(guǎng)播通知的方(fāng)式,通知給所有消費節(jiē)點,消費節點收到消息(xī)以後(hòu),執行對應邏輯刷新本(běn)地緩存。該方式存在一定缺陷,如果期間服務異常,或者服務進程在做Crash,可能收(shōu)到通知(zhī)後處理失敗,失敗後沒有重試機製。

    消(xiāo)息發布/訂閱,redis具備消息發布/訂閱能(néng)力,事務執行節點寫入一條消息,各應用節點進行訂閱消費,接收消息後進行刷新邏輯(jí)執行,redis消(xiāo)息滿足了(le)絕大部分場(chǎng)景,但是如果為了(le)提高穩定性可(kě)以采用消息中間件代替。


  • 更新緩存操作事務一致性問題

    更新緩存時,業務係統可能存在數據庫更新(xīn)操作,數據庫更新成功(gōng)後,更新緩存可能存在外部接口調用獲取緩存更新依(yī)賴的數據,但是外部接口一旦失敗,事物上缺少控製,就會導致數(shù)據庫更新(xīn)成功,緩存更新失敗的情況(kuàng)。這種情況需要業務係統對事物中的所有異常進行捕獲,規避不一致的風(fēng)險。


2

緩存穿透(tòu)問題

緩存穿透指查詢一個一(yī)定不存在的數據,由於緩存沒命中,將去查數據庫,但是數據庫也無此記錄,這將(jiāng)導致每次請(qǐng)求都打到(dào)了數據(jù)庫,失去了緩存的意義(yì)。緩存穿透可能會使數據庫負載加大,由於數據庫在高並發下性能較差,甚至可能造成數據庫宕機,該場景在項目上(shàng)經常碰見。

圖片(piàn)關鍵(jiàn)詞


案例:


某省份項目由於(yú)訂單處理時,從緩存中沒有讀取到規(guī)則配置數據,執行了從數(shù)據庫加(jiā)載全量配置,數據庫中也沒有對應配置數據,導致每次請求都會全量加載一遍規(guī)則配置數據,嚴重影響了訂單處理性能(néng),對數據庫性能也產生(shēng)了(le)較大影(yǐng)響。


解決方案:

通常可以在程序中(zhōng)統計總調用數、緩存層命中(zhōng)數、如果同一個Key的緩存命中率很低,可(kě)能就是出現了緩存穿透問題。


一般(bān)可以通過如下方案解決:

  • 設置NullObject,訪問數據庫miss時,設置(zhì)一個空對象到緩存中,防止下次繼續(xù)請求數據庫。該方法(fǎ)實現簡單,但也存在一定(dìng)的缺(quē)陷,需要業務側自行評估。一是,如果miss數據很多,大量key寫入緩存,會占用內存空間。二是,數據一致性問題,如果NullObject設置以後,數據庫新增(zēng)了數據,無法自動(dòng)更新到緩存,需要業務側額外(wài)實現邏輯進行更新。

  • 布隆過濾器,其實就是(shì)在訪問緩存層(céng)和數據庫之前,將存(cún)在的key用布(bù)隆(lóng)過濾器提前保存起來,做第一層攔截(當收到一個對key請求時先用布隆過濾器驗證是key否存(cún)在,如果存在再進入緩存層、存(cún)儲層)。布隆過濾器優點是空間效率和查詢時間都遠遠超過一般的算法,缺點是(shì)有一定的誤識別(bié)率和刪除困難。


3

緩存(cún)雪崩問題

緩(huǎn)存充當了數據庫訪問的保護層,防止數據(jù)庫訪問壓力過大而宕機,但是如果緩存(cún)出現宕機,或(huò)大批量同時失效(xiào),大(dà)量請求打到數據庫,導致數(shù)據庫負荷突然拉高,壓力過大(dà)而導致雪崩。(該問題(tí)在項目上(shàng)出現(xiàn)的概率也不低,一(yī)般是緩存時效時間設置不合理導致(zhì)。)


案例:

某省份項目由於樓層(céng)數據緩存采用固定有效期,一(yī)次版本(běn)升級以(yǐ)後,緩存數據全量做了一次更新,在失效期到了以後,所有緩存失效,頁麵請求同一時間點全部打到數據庫加載緩存數據,導致數據庫壓力瞬間過大(dà),影響整個係統性能響應。


解決方案:

針(zhēn)對緩存雪崩,通過如(rú)下(xià)方案可規避:

  • 緩存服務搭建(jiàn)采用高可(kě)用模(mó)式(shì),防止單節點宕機導致整(zhěng)個服務受影(yǐng)響。

  • 采用多級緩(huǎn)存,本地進程作(zuò)為一級緩存,redis作為二級緩存,不同級別的緩存設置的超時時間不同,即使某級緩存過期了,也有其他級別緩存兜(dōu)底。

  • 緩存的過期時間使用固定值+隨(suí)機值,盡量讓不同的key的過期(qī)時間不同。


4

序列化問題

序列化主要的用處就(jiù)是在傳遞和保存對象的時候(hòu),保證對象的完(wán)整(zhěng)性(xìng)和可傳(chuán)遞性。序列化是把對象轉換(huàn)成有序字節(jiē)流,以便在網絡上傳輸。反序列化便是根據網絡傳輸(shū)字節流中所保存(cún)的對象狀態及描述信息,通過反序列化重建對象(xiàng)。

在保存對象和重建對象過程中,不同序列化工具對描(miáo)述信息差異容忍度(dù)不一致、性能不一致,容易出現問題(tí)。


案例(lì):

  • 某產品緩存序列化工具(jù)采用kryo,某次版本修改了(le)對象屬(shǔ)性,版本發布時忘記清理已有緩存,導致存量數據在(zài)反序列化時全部報(bào)錯,造(zào)成生產故(gù)障。

  • 某門戶(hù)產品,由於(yú)會話用戶信息使(shǐ)用(yòng)kryo存儲,多係統(tǒng)共用會話緩存,某次門戶升級增加了用戶屬性,未(wèi)及時通知下遊係統升級,導致升級(jí)後下遊係統出(chū)現用戶(hù)信息反序列失敗的故障。

解決方案:

分布式緩存訪問的,涉及實體對象,必須通過序列、反序列化來進行存取,實體對象一般(bān)映射數據庫模型,在業務需求變更時(shí),模(mó)型字段發生了變化,對於已經寫入的緩存,部分序列化工具會存在(zài)反序列失敗的問(wèn)題。同(tóng)時不同序列化工具在性能上麵也存在一定差異,業務側根據各自情況進行選擇使用。


5

熱key問題

所謂熱key問題就是,突然有幾十萬的請求去訪問redis上的某個特定key。那麽,這樣會造成流(liú)量過於集中,達到物理(lǐ)網(wǎng)卡上(shàng)限,從而導致這(zhè)台redis的服務器宕機。


案例:

某項(xiàng)目一個靜態配置數據放(fàng)在了redis緩存,業(yè)務規(guī)則處理(lǐ)中存在大循(xún)環調用,一次業務處(chù)理重複獲取了幾百次(cì)靜態數據(jù),業務量大時導致該key的訪問非(fēi)常頻繁。


解決方案(àn):

  • 將每(měi)次業務訪問的key進(jìn)行拆分,避免總是訪(fǎng)問同一個key。

  • 對需要頻繁訪問的key進(jìn)行本地緩存,本地緩存數據(jù)可以通過定時策略進行更(gèng)新。

  • 優化業務處理邏輯,減少無效交(jiāo)互訪(fǎng)問:例如一個服務裏麵有N次訪問某個配置的邏輯,那麽在服務(wù)邏輯開始時從緩存裏麵取一次(cì)配置就好,避(bì)免單一(yī)服務大量重複緩存(cún)交(jiāo)互(hù)。


6

分布式鎖死(sǐ)鎖問題

在分布式高並發的條件下,如果有個線程獲(huò)得鎖的同時,還沒有來得及(jí)去釋放鎖,就因為係統故(gù)障或者其它原因使它無法執行釋放鎖的命令,導致其它線程都無(wú)法獲得鎖,造成死鎖。


案例:

某采(cǎi)購(gòu)項目,為了避(bì)免訂單、購物車重複提交(jiāo),在服務入口處,使用SETNX獲取分布(bù)式鎖,在(zài)服務出口進行分布式鎖解鎖操作,由於沒有考慮執行異常的情況,異常(cháng)後沒有執(zhí)行(háng)解鎖,導致鎖一直無法釋放。


解決方案:

項目上使用分布式鎖經常出現死鎖,或者鎖失效的情(qíng)況,基本都是在(zài)使用原理及業務場景匹配上存在不清晰所導致,一把穩定的分布式鎖一般具有如下特(tè)征:


互斥性(xìng), 任意時刻,隻有一個客戶端能持有鎖。


鎖超時釋放,持有(yǒu)鎖超時,可以釋放,防止不必要的資源浪費,也可以防止死鎖。


可重入性,一個線程如果獲(huò)取了(le)鎖之後(hòu),可以再次對其請求加鎖。


高性能和高可用(yòng),加(jiā)鎖和解鎖需要(yào)開銷盡可能低,同時也要保證高可用,避(bì)免分布式鎖失效(xiào)。


安全性,鎖隻能被持(chí)有的客戶端刪除,不能被其他客戶端刪除(chú)。


從實現上,一般有如下幾(jǐ)種方案:


SETNX + EXPIRE,SETNX獲(huò)取鎖以後(hòu),再使用EXPIRE進行過期時間(jiān)設置,防止客戶端崩潰後,鎖無法釋放,但是這樣存在問題,SETNX + EXPIRE並非原子操作,如果發送EXPIRE時正(zhèng)好應用Crash,一樣(yàng)會導致(zhì)死鎖。


使用Lua腳本(包含SETNX + EXPIRE兩條指令),通過Lua腳(jiǎo)本執行,保證了兩(liǎng)條(tiáo)指令的原子(zǐ)性(xìng)。但還是存在一定風險,比如A線程鎖到期釋放了,但是業務邏(luó)輯還沒執行完,導致B線程又重新(xīn)獲取了鎖,最後(hòu)B線程把A線程的(de)鎖給刪除。


使用Lua腳本(SET EX PX NX + 校驗(yàn)唯(wéi)一隨機值(zhí),再釋放(fàng)鎖),采用set命令,結合擴(kuò)展參數,同時通過唯一隨機值校驗,解決了鎖被誤刪的情況,但是這樣還是解決(jué)不了鎖過期釋放而業務沒有(yǒu)執行(háng)完的問(wèn)題。


開源框架Redisson,通過開啟守護線程,每隔一段時間檢查鎖是否(fǒu)還存在(zài),存在則對鎖的過期(qī)時間延長(zhǎng),防(fáng)止鎖(suǒ)過期提前釋放。但是該方案在集群模式下,會存在(zài)同步(bù)延時的問(wèn)題。


實現的分布式鎖Redlock,由Redis作(zuò)者antirez提出一種(zhǒng)高級的分(fèn)布式鎖算法,按順序向多個master節點獲取鎖,按一定(dìng)比例成功率進行計(jì)算(suàn)。





總(zǒng)結




緩存使用場景及使(shǐ)用中可能遇到的問題,遠不止上麵列(liè)出來的內容,需要注(zhù)意的點非常多,所以我們在引入時,或者用(yòng)到其中的特性要從整體去看,了解相關原理、適用場景、注意事項,多方麵規避(bì)風險,總結下來,在緩存使用(yòng)過程中,應該要從以下(xià)方(fāng)麵進行考(kǎo)慮:


設計(jì)上確(què)保穩定、安全、高性能

  • 根據業務(wù)特性、體量等,合理選擇緩存產品

  • 根據緩(huǎn)存產品特性,結合業務(wù)對穩定性、性能等(děng)方麵的(de)要求(qiú),對部署架構進行規劃評估

  • 結合安全管控要求,在賬號管理、網絡、災備(bèi)方麵進行安(ān)全設計

  • 結合業務容忍度,在一致性(xìng)、健壯性上進行分析考慮,同時要規避(bì)一些風(fēng)險命令的使用

  • 做好(hǎo)緩存數據生命(mìng)周期的規劃,不同業務數據設計合適的生命周期

  • 做好key、value設計,易維護,合理選擇數據類型


研發上注意關鍵配置,熟悉產品(pǐn)特性

  • 注意(yì)緩存連接池的管理,根據業務指標評估(gū)生產所需的最大連接數(shù)、空閑(xián)連接數等

  • 熟悉每個緩存產品的研發指導,熟悉相關API、命令(lìng)及(jí)適用場景


運維上確保快速響應、勤(qín)總結

  • 根據不同的緩存(cún)產品,選擇(zé)合適的監控工具(jù),熟悉其監控工具

  • 熟悉(xī)緩存產品(pǐn)相關監控指標(biāo)

  • 勤於總結相關運維問題(tí),沉澱知識庫,不斷提升運維質(zhì)量及效率


官方微信公眾號

国产亚洲熟妇在线视频雲計算科(kē)技股份(fèn)有限公司 版權所有 2003-2023

蘇ICP備10224443號-6       蘇公網安備(bèi) 32011402011374號

国产亚洲熟妇在线视频-亚洲熟妇AV乱码在线观看-亚州国产AV一区二区三区伊在-中文字幕无码人妻少妇免费视频-欧美 日韩 人妻 高清 中文-熟妇人妻中文字幕无码老熟妇-丰满熟女人妻一区二区三-亚洲精品字幕