Redis作為緩存應(yīng)用的情形詳細(xì)分析
為什么使用緩存
Redis是一個(gè)內(nèi)存型數(shù)據(jù)庫(kù),也就是說(shuō),所有的數(shù)據(jù)都會(huì)存在與內(nèi)存中,基于Redis的高性能特性,我們將Redis用在緩存場(chǎng)景非常廣泛。使用起來(lái)方便,響應(yīng)也是遠(yuǎn)超關(guān)系型數(shù)據(jù)庫(kù)。
應(yīng)用場(chǎng)景
Redis的應(yīng)用場(chǎng)景非常廣泛。雖然Redis是一個(gè)key-value的內(nèi)存數(shù)據(jù)庫(kù),但在實(shí)際場(chǎng)景中,Redis經(jīng)常被作為緩存來(lái)使用,如面對(duì)數(shù)據(jù)高并發(fā)的讀寫、海量數(shù)據(jù)的讀寫等。
舉個(gè)例子,A網(wǎng)站首頁(yè)一天有100萬(wàn)人訪問(wèn),其中有一個(gè)“積分商城”的板塊,要直接從數(shù)據(jù)庫(kù)查詢,那么一天就要多消耗100萬(wàn)次數(shù)據(jù)庫(kù)請(qǐng)求。如果將這些數(shù)據(jù)儲(chǔ)存到Redis(內(nèi)存)中,要用的時(shí)候,直接從內(nèi)存調(diào)取,不僅可以大大節(jié)省系統(tǒng)直接讀取磁盤來(lái)獲得數(shù)據(jù)的IO開(kāi)銷,提高服務(wù)器的資源利用率,還能極大地提升速度。
比如很多大型電商網(wǎng)站、視頻網(wǎng)站和游戲應(yīng)用等,存在大規(guī)模數(shù)據(jù)訪問(wèn),對(duì)數(shù)據(jù)查詢效率要求高。Redis服務(wù)可實(shí)現(xiàn)頁(yè)面緩存、應(yīng)用緩存、狀態(tài)緩存、事件并行處理,能夠有效減少數(shù)據(jù)庫(kù)磁盤IO,提高數(shù)據(jù)查詢效率,減輕管理維護(hù)工作量,降低數(shù)據(jù)庫(kù)存儲(chǔ)成本。對(duì)傳統(tǒng)磁盤數(shù)據(jù)庫(kù)是一個(gè)重要的補(bǔ)充,成為了互聯(lián)網(wǎng)應(yīng)用,尤其是支持高并發(fā)訪問(wèn)的互聯(lián)網(wǎng)應(yīng)用必不可少的基礎(chǔ)服務(wù)之一。
具體而言,分布式緩存Redis可用于以下場(chǎng)景:
1、頁(yè)面緩存
Redis可將Web頁(yè)面的內(nèi)容片段,包括HTML,CSS和圖片等靜態(tài)數(shù)據(jù),緩存到Redis實(shí)例,提高網(wǎng)站的訪問(wèn)性能。
比如在電商類應(yīng)用中,熱銷商品展示、秒殺推薦等數(shù)據(jù)面臨高并發(fā)讀的壓力,分布式緩存Redis的高并發(fā)及靈活擴(kuò)展,可輕松支持此類應(yīng)用。
2、狀態(tài)緩存
Redis可將Session會(huì)話狀態(tài)及應(yīng)用橫向擴(kuò)展時(shí)的狀態(tài)數(shù)據(jù)等緩存到DCS實(shí)例,實(shí)現(xiàn)狀態(tài)數(shù)據(jù)共享。在應(yīng)對(duì)游戲應(yīng)用中爆發(fā)式增長(zhǎng)的玩家數(shù)據(jù)存儲(chǔ)和讀寫請(qǐng)求時(shí),使用分布式緩存Redis可通過(guò)將熱點(diǎn)數(shù)據(jù)放入緩存,加快用戶端訪問(wèn)速度,提升用戶體驗(yàn)。
3、應(yīng)用對(duì)象緩存
Redis可作為服務(wù)層的二級(jí)緩存對(duì)外提供服務(wù),減輕數(shù)據(jù)庫(kù)的負(fù)載壓力,加速應(yīng)用訪問(wèn)。
4、事件緩存
Redis可提供針對(duì)事件流的連續(xù)查詢(continuous query)處理技術(shù),滿足實(shí)時(shí)性需求。
使用緩存的收益和成本
如圖左側(cè)為客戶端直接調(diào)用存儲(chǔ)層的架構(gòu),右側(cè)為比較典型的緩存層+存儲(chǔ)層架構(gòu),下面分析一下緩存加入后帶來(lái)的收益和成本。
收益:
l 加速讀寫:因?yàn)榫彺嫱ǔ6际侨珒?nèi)存的,而存儲(chǔ)層通常讀寫性能不夠強(qiáng)悍(例如MySQL),通過(guò)緩存的使用可以有效地加速讀寫,優(yōu)化用戶體驗(yàn)。
l 降低后端負(fù)載:幫助后端減少訪問(wèn)量和復(fù)雜計(jì)算(例如很復(fù)雜的SQL語(yǔ)句),在很大程度降低了后端的負(fù)載。
成本:
l 數(shù)據(jù)不一致性:緩存層和存儲(chǔ)層的數(shù)據(jù)存在著一定時(shí)間窗口的不一致性,時(shí)間窗口跟更新策略有關(guān)。
l 代碼維護(hù)成本:加入緩存后,需要同時(shí)處理緩存層和存儲(chǔ)層的邏輯,增大了開(kāi)發(fā)者維護(hù)代碼的成本。
l 運(yùn)維成本:以Redis Cluster為例,加入后無(wú)形中增加了運(yùn)維成本。
緩存不一致
一致性
1、強(qiáng)一致性
如果你的項(xiàng)目對(duì)緩存的要求是強(qiáng)一致性的,那么請(qǐng)不要使用緩存。這種一致性級(jí)別是最符合用戶直覺(jué)的,它要求系統(tǒng)寫入什么,讀出來(lái)的也會(huì)是什么,用戶體驗(yàn)好,但實(shí)現(xiàn)起來(lái)往往對(duì)系統(tǒng)的性能影響大。
2、弱一致性
這種一致性級(jí)別約束了系統(tǒng)在寫入成功后,不承諾立即可以讀到寫入的值,也不承諾多久之后數(shù)據(jù)能夠達(dá)到一致,但會(huì)盡可能地保證到某個(gè)時(shí)間級(jí)別(比如秒級(jí)別)后,數(shù)據(jù)能夠達(dá)到一致?tīng)顟B(tài)
3**、最終一致性**
最終一致性是弱一致性的一個(gè)特例,系統(tǒng)會(huì)保證在一定時(shí)間內(nèi),能夠達(dá)到一個(gè)數(shù)據(jù)一致的狀態(tài)。這里之所以將最終一致性單獨(dú)提出來(lái),是因?yàn)樗侨跻恢滦灾蟹浅M瞥绲囊环N一致性模型,也是業(yè)界在大型分布式系統(tǒng)的數(shù)據(jù)一致性上比較推崇的模型。一般情況下,高可用只確保最終一致性,不確保強(qiáng)一致性。
強(qiáng)一致性,讀請(qǐng)求和寫請(qǐng)求會(huì)串行化,串到一個(gè)內(nèi)存隊(duì)列里去,這樣會(huì)大大增加系統(tǒng)的處理效率,吞吐量也會(huì)大大降低。
業(yè)務(wù)場(chǎng)景
在絕大多數(shù)的系統(tǒng)中數(shù)據(jù)庫(kù)往往是用戶并發(fā)訪問(wèn)最薄弱的地方,并且在高并發(fā)下的讀多寫少的情況下,我們往往會(huì)借助一些中間鍵,來(lái)解決數(shù)據(jù)訪問(wèn)過(guò)大時(shí)造成的數(shù)據(jù)庫(kù)宕機(jī)情況,例如我們可以使用Redis來(lái)作為緩存,讓請(qǐng)求先訪問(wèn)到Redis,而不是直接訪問(wèn)數(shù)據(jù)庫(kù)。而在這種業(yè)務(wù)場(chǎng)景下,可能會(huì)出現(xiàn)緩存和數(shù)據(jù)庫(kù)數(shù)據(jù)不一致性的問(wèn)題。
問(wèn)題產(chǎn)生的原因
一般來(lái)說(shuō)讀取緩存步驟是不會(huì)有什么問(wèn)題的,但是一旦涉及到數(shù)據(jù)更新,也就是數(shù)據(jù)庫(kù)和緩存都操作,就容易出現(xiàn)緩存(Redis)和數(shù)據(jù)庫(kù)(MySQL)間的數(shù)據(jù)一致性問(wèn)題。
在數(shù)據(jù)更新時(shí),我們需要做以下兩步:
- 操作MySQL
- 操作緩存
但是無(wú)論是先執(zhí)行步驟1還是先執(zhí)行步驟2,都有可能出現(xiàn)數(shù)據(jù)不一致的情況,主要是因?yàn)樽x寫是并發(fā)的,我們無(wú)法保證他們的先后順序。
相關(guān)策略
先做一個(gè)說(shuō)明,從理論上來(lái)說(shuō),給緩存設(shè)置過(guò)期時(shí)間,是保證最終一致性的解決方案(如果要求強(qiáng)一致性的話,我認(rèn)為沒(méi)有必要添加緩存了,直接走數(shù)據(jù)庫(kù))。這種前提下,我們可以對(duì)存入緩存的數(shù)據(jù)設(shè)置過(guò)期時(shí)間,所有的寫操作以數(shù)據(jù)庫(kù)為準(zhǔn),對(duì)緩存操作只是盡最大努力即可。也就是說(shuō)如果數(shù)據(jù)庫(kù)寫成功,緩存更新失敗,那么只要到達(dá)過(guò)期時(shí)間,則后面的讀請(qǐng)求自然會(huì)從數(shù)據(jù)庫(kù)中讀取新值然后回填緩存。因此,接下來(lái)討論的思路不依賴于給緩存設(shè)置過(guò)期時(shí)間這個(gè)方案。
給出了三種更新策略:
- 先更新數(shù)據(jù)庫(kù),再更新緩存
- 先刪除緩存,再更新數(shù)據(jù)庫(kù)
- 先更新數(shù)據(jù)庫(kù),在刪除緩存
先更新數(shù)據(jù)庫(kù)值再更新緩存值
最不可能選擇的策略,原因是此種策略可能會(huì)在線程安全的角度和業(yè)務(wù)場(chǎng)景角度生成臟數(shù)據(jù)和性能問(wèn)題。
原因一:線程安全的角度
同時(shí)有請(qǐng)求A和請(qǐng)求B進(jìn)行更新操作,那么就會(huì)出現(xiàn)
- 請(qǐng)求A更新數(shù)據(jù)庫(kù)
- 請(qǐng)求B更新數(shù)據(jù)庫(kù)
- 請(qǐng)求B更新緩存
- 請(qǐng)求A更新緩存
這就出現(xiàn)請(qǐng)求A更新緩存應(yīng)該比請(qǐng)求B更新緩存早才對(duì),但是因?yàn)榫W(wǎng)絡(luò)等原因,B比A更早更新了緩存。這就導(dǎo)致了臟數(shù)據(jù),因此不考慮。
業(yè)務(wù)場(chǎng)景角度
(1)如果是寫數(shù)據(jù)庫(kù)場(chǎng)景比較多,而讀數(shù)據(jù)場(chǎng)景比較少的業(yè)務(wù)需求,那么采用這種方案就會(huì)導(dǎo)致,數(shù)據(jù)壓根還沒(méi)讀到,緩存就被頻繁的更新,浪費(fèi)性能,緩存此類數(shù)據(jù),沒(méi)有很大的意義。
(2)如果是寫入數(shù)據(jù)庫(kù)的值,并不是直接寫入緩存的,而是要經(jīng)過(guò)一系列復(fù)雜的計(jì)算再寫入緩存。那么,每次寫入數(shù)據(jù)庫(kù)后,都再次計(jì)算寫入緩存的值,無(wú)疑是浪費(fèi)性能的。顯然,刪除緩存更為適合。
后面兩種策略都是對(duì)緩存進(jìn)行刪除,這里先做一個(gè)解釋。
例子:數(shù)據(jù)庫(kù)在1小時(shí)內(nèi)更新1000次那么緩存也更新1000次,但是這個(gè)緩存可能在1小時(shí)內(nèi)只被讀了1次,那么就沒(méi)有必要更新1000次了。反過(guò)來(lái),如果是刪除的話,那么也只是做了1次刪除操作,當(dāng)緩存真正被讀取的時(shí)候才去更新。
刪除緩存值再更新數(shù)據(jù)庫(kù)值
- 請(qǐng)求A進(jìn)行更新操作,首先刪除緩存
- 請(qǐng)求B查詢發(fā)現(xiàn)緩存不存在
- 請(qǐng)求B去數(shù)據(jù)庫(kù)查詢得到舊值
- 請(qǐng)求B將舊值寫入緩存
- 請(qǐng)求A將新值寫入數(shù)據(jù)庫(kù)
上述情況就會(huì)導(dǎo)致不一致的情形出現(xiàn)。而且,如果不采用給緩存設(shè)置過(guò)期時(shí)間策略,該數(shù)據(jù)永遠(yuǎn)都是臟數(shù)據(jù)。我們可以采用延遲雙刪策略,來(lái)解決這個(gè)問(wèn)題。
相對(duì)應(yīng)的步驟:
- 先淘汰緩存
- 再寫數(shù)據(jù)庫(kù)
- 休眠t秒,再次淘汰緩存
這么做的目的,就是確保讀請(qǐng)求結(jié)束,寫請(qǐng)求可以刪除讀請(qǐng)求造成的緩存臟數(shù)據(jù)。
// 偽代碼 public void updateDb(String key,Object data) { redis.delKey(key); db.updateData(data); Thread.sleep(t); redis.delKey(key); }
如果系統(tǒng)中MySQL使用了讀寫分離模式,那么有可能會(huì)出現(xiàn)在主從同步?jīng)]有完成時(shí),讀請(qǐng)求就去讀取數(shù)據(jù)了,這時(shí)候就會(huì)讀取到舊值,這里我們可以延長(zhǎng)睡眠時(shí)間,讓主從同步完成后在進(jìn)行一次刪除(如果不考慮主從的情況下,采用雙刪不用加延時(shí)時(shí)間也是可以保證一直性的)。
先更新數(shù)據(jù)庫(kù)值在刪除緩存值
假設(shè)有兩個(gè)請(qǐng)求,請(qǐng)求A進(jìn)行更新操作,請(qǐng)求B進(jìn)行查詢操作。
那么會(huì)出現(xiàn)如下情形:
- 請(qǐng)求A進(jìn)行更新操作,首先更新數(shù)據(jù)庫(kù)
- 請(qǐng)求B進(jìn)行查詢操作,擊中緩存,得到舊值
- 請(qǐng)求A進(jìn)行刪除緩存操作
在這種情況下如果其他線程并發(fā)讀緩存的請(qǐng)求不多,那么,就不會(huì)有很多請(qǐng)求讀取到舊值。而且,請(qǐng)求 A 一般也會(huì)很快刪除緩存值,這樣一來(lái),其他線程再次讀取時(shí),就會(huì)發(fā)生緩存缺失,進(jìn)而從數(shù)據(jù)庫(kù)中讀取最新值。所以,這種情況對(duì)業(yè)務(wù)的影響較小。
無(wú)論是策略2還是策略3都有可能會(huì)出現(xiàn)這種情況:刪除緩存失敗,這時(shí)我們可以采用重試機(jī)制來(lái)保證數(shù)據(jù)的一致性。
方案的詳細(xì)設(shè)計(jì)
在相關(guān)策略的調(diào)用中,雖然提出了一些簡(jiǎn)單解決方案,但是沒(méi)有考慮到列如 緩存刪除失敗,數(shù)據(jù)庫(kù)更新失敗等情況,因此需要增加重試策略,但是還是可能會(huì)出現(xiàn)比較不一致的問(wèn)題,此處詳細(xì)介紹幾種方案。
流程如下:
- 更新數(shù)據(jù)庫(kù)數(shù)據(jù);
- 緩存因?yàn)榉N種問(wèn)題刪除失敗
- 將需要?jiǎng)h除的key發(fā)送至消息隊(duì)列
- 自己消費(fèi)消息,獲得需要?jiǎng)h除的key
- 繼續(xù)重試刪除操作,直到成功
如果能夠成功地刪除或更新,我們就要把這些值從消息隊(duì)列中去除,以免重復(fù)操作,此時(shí),我們也可以保證數(shù)據(jù)庫(kù)和緩存的數(shù)據(jù)一致了。否則的話,我們還需要再次進(jìn)行重試。如果重試超過(guò)的一定次數(shù),還是沒(méi)有成功,我們就需要向業(yè)務(wù)層發(fā)送報(bào)錯(cuò)信息了。
// 偽代碼 public void updateDb(String key,Object data){ db.updateData(data); if (!redis.delKey(key)) { mq.send(key); new Thread(() -> asyncDel()).start(); } } // 異步重試 private void asyncDel() { int count = 0; String key = mq.get(); while(!redis.delKey(key)) { count++; if (count > 5) { throw new DelFailException(); } } mq.remove(key); }
這種雖然可以解決,但是會(huì)對(duì)業(yè)務(wù)代碼造成侵入,而且還需要去維護(hù)消息隊(duì)列,如果可以容忍的話,我覺(jué)得是可選的方案之一。
注意 需要使用有序的消息隊(duì)列,保證消息的有序性。重試刪除
訂閱binlog
業(yè)務(wù)代碼只會(huì)操作數(shù)據(jù)庫(kù),不操作緩存。同時(shí)啟動(dòng)一個(gè)訂閱binlog的程序去監(jiān)聽(tīng)刪除操作,然后投遞到消息隊(duì)列中。再啟動(dòng)一個(gè)消費(fèi)者,根據(jù)消息去刪除緩存。
canal是用來(lái)模擬MySQL slave,來(lái)訂閱MySQL master 的binlog。
異步重試
總結(jié)
對(duì)于讀多寫少的數(shù)據(jù),請(qǐng)使用緩存。
為了保持?jǐn)?shù)據(jù)庫(kù)和緩存的一致性,會(huì)導(dǎo)致系統(tǒng)吞吐量的下降。
為了保持?jǐn)?shù)據(jù)庫(kù)和緩存的一致性,會(huì)導(dǎo)致業(yè)務(wù)代碼邏輯復(fù)雜。
緩存做不到絕對(duì)一致性,但可以做到最終一致性。
對(duì)于需要保證緩存數(shù)據(jù)庫(kù)數(shù)據(jù)一致的情況,請(qǐng)盡量考慮對(duì)一致性到底有多高要求,選定合適的方案,避免過(guò)度設(shè)計(jì)。
緩存問(wèn)題
緩存穿透
問(wèn)題描述
緩存穿透是指查詢一個(gè)根本不存在的數(shù)據(jù),緩存層和存儲(chǔ)層都不會(huì)命中,通常出于容錯(cuò)的考慮,如果從存儲(chǔ)層查不到數(shù)據(jù)則不寫入緩存層,如下圖所示
整個(gè)過(guò)程分為如下3步:
緩存層不命中。存儲(chǔ)層不命中,不將空結(jié)果寫回緩存。返回空結(jié)果。
緩存穿透將導(dǎo)致不存在的數(shù)據(jù)每次請(qǐng)求都要到存儲(chǔ)層去查詢,失去了緩存保護(hù)后端存儲(chǔ)的意義。
緩存穿透問(wèn)題可能會(huì)使后端存儲(chǔ)負(fù)載加大,由于很多后端存儲(chǔ)不具備高并發(fā)性,甚至可能造成后端存儲(chǔ)宕掉。通常可以在程序中分別統(tǒng)計(jì)總調(diào)用數(shù)、緩存層命中數(shù)、存儲(chǔ)層命中數(shù),如果發(fā)現(xiàn)大量存儲(chǔ)層空命中,可能就是出現(xiàn)了緩存穿透問(wèn)題。
解決方案
造成緩存穿透的基本原因有兩個(gè)。第一,自身業(yè)務(wù)代碼或者數(shù)據(jù)出現(xiàn)問(wèn)題,第二,一些惡意攻擊、爬蟲(chóng)等造成大量空命中。下面我們來(lái)看一下如何解決緩存穿透問(wèn)題。
緩存空對(duì)象
如圖所示,當(dāng)?shù)?步存儲(chǔ)層不命中后,仍然將空對(duì)象保留到緩存層中,之后再訪問(wèn)這個(gè)數(shù)據(jù)將會(huì)從緩存中獲取,這樣就保護(hù)了后端數(shù)據(jù)源。
緩存空對(duì)象會(huì)有兩個(gè)問(wèn)題:第一,空值做了緩存,意味著緩存層中存了更多的鍵,需要更多的內(nèi)存空間,比較有效的方法是針對(duì)這類數(shù)據(jù)設(shè)置一個(gè)較短的過(guò)期時(shí)間,讓其自動(dòng)剔除。第二,緩存層和存儲(chǔ)層的數(shù)據(jù)會(huì)有一段時(shí)間窗口的不一致,可能會(huì)對(duì)業(yè)務(wù)有一定影響。例如過(guò)期時(shí)間設(shè)置為5分鐘,如果此時(shí)存儲(chǔ)層添加了這個(gè)數(shù)據(jù),那此段時(shí)間就會(huì)出現(xiàn)緩存層和存儲(chǔ)層數(shù)據(jù)的不一致,此時(shí)可以利用消息系統(tǒng)或者其他方式清除掉緩存層中的空對(duì)象。
布隆過(guò)濾器攔截
布隆過(guò)濾器:實(shí)際上是一個(gè)很長(zhǎng)的二進(jìn)制向量和一系列隨機(jī)映射函數(shù)。布隆過(guò)濾器可以用于檢索一個(gè)元素是否在一個(gè)集合中。它的優(yōu)點(diǎn)是空間效率和查詢時(shí)間都比一般的算法要好的多,缺點(diǎn)是有一定的誤識(shí)別率和刪除困難??梢愿嬖V你某樣?xùn)|西一定不存在或者可能存在。
如圖所示,在訪問(wèn)緩存層和存儲(chǔ)層之前,將存在的key用布隆過(guò)濾器提前保存起來(lái),做第一層攔截。例如:一個(gè)推薦系統(tǒng)有4億個(gè)用戶id,每個(gè)小時(shí)算法工程師會(huì)根據(jù)每個(gè)用戶之前歷史行為計(jì)算出推薦數(shù)據(jù)放到存儲(chǔ)層中,但是最新的用戶由于沒(méi)有歷史行為,就會(huì)發(fā)生緩存穿透的行為,為此可以將所有推薦數(shù)據(jù)的用戶做成布隆過(guò)濾器。如果布隆過(guò)濾器認(rèn)為該用戶id不存在,那么就不會(huì)訪問(wèn)存儲(chǔ)層,在一定程度保護(hù)了存儲(chǔ)層。
兩種方案比對(duì)
緩存雪崩
如圖描述了什么是緩存雪崩:由于緩存層承載著大量請(qǐng)求,有效地保護(hù)了存儲(chǔ)層,但是如果緩存層由于某些原因不能提供服務(wù),于是所有的請(qǐng)求都會(huì)達(dá)到存儲(chǔ)層,存儲(chǔ)層的調(diào)用量會(huì)暴增,造成存儲(chǔ)層也會(huì)級(jí)聯(lián)宕機(jī)的情況。
預(yù)防和解決緩存雪崩問(wèn)題,可以從以下三個(gè)方面進(jìn)行著手。
(1) 保證緩存層服務(wù)高可用性。如果緩存層設(shè)計(jì)成高可用的,即使個(gè)別節(jié)點(diǎn)、個(gè)別機(jī)器、甚至是機(jī)房宕掉,依然可以提供服務(wù),例如前面介紹過(guò)的Redis Sentinel和Redis Cluster都實(shí)現(xiàn)了高可用。
(2) 依賴隔離組件為后端限流并降級(jí)。無(wú)論是緩存層還是存儲(chǔ)層都會(huì)有出錯(cuò)的概率,可以將它們視同為資源。作為并發(fā)量較大的系統(tǒng),假如有一個(gè)資源不可用,可能會(huì)造成線程全部阻塞在這個(gè)資源上,造成整個(gè)系統(tǒng)不可用。降級(jí)機(jī)制在高并發(fā)系統(tǒng)中是非常普遍的。實(shí)際項(xiàng)目中,我們需要對(duì)重要的資源(例如Redis、MySQL、HBase、外部接口)都進(jìn)行隔離,讓每種資源都單獨(dú)運(yùn)行在自己的線程池中,即使個(gè)別資源出現(xiàn)了問(wèn)題,對(duì)其他服務(wù)沒(méi)有影響。但是線程池如何管理,比如如何關(guān)閉資源池、開(kāi)啟資源池、資源池閥值管理,這些做起來(lái)還是相當(dāng)復(fù)雜的。這里推薦使用Java依賴隔離工具Hystrix,他是解決依賴隔離的利器。
(3) 提前演練。在項(xiàng)目上線前,演練緩存層宕掉后,應(yīng)用以及后端的負(fù)載情況以及可能出現(xiàn)的問(wèn)題,在此基礎(chǔ)上做一些預(yù)案設(shè)定。
緩存擊穿(熱點(diǎn)數(shù)據(jù)集中失效)
問(wèn)題描述
當(dāng)一個(gè)key是熱點(diǎn)key,并發(fā)量很大,而且重建緩存不能在短時(shí)間完成,在緩存失效的一瞬間,就會(huì)有大量的線程來(lái)重建緩存,造成后端負(fù)載加大,甚至讓應(yīng)用崩潰,這就叫緩存擊穿。如下圖:
解決方案
互斥鎖
此方法只允許一個(gè)線程重建緩存,其他線程等待重建緩存的線程執(zhí)行完,重新從緩存獲取數(shù)據(jù)即可,整個(gè)過(guò)程如圖所示。
永遠(yuǎn)不過(guò)期
“永遠(yuǎn)不過(guò)期”包含兩層意思:
l 從緩存層面來(lái)看,確實(shí)沒(méi)有設(shè)置過(guò)期時(shí)間,所以不會(huì)出現(xiàn)熱點(diǎn)key過(guò)期后產(chǎn)生的問(wèn)題,也就是“物理”不過(guò)期。
l 從功能層面來(lái)看,為每個(gè)value設(shè)置一個(gè)邏輯過(guò)期時(shí)間,當(dāng)發(fā)現(xiàn)超過(guò)邏輯過(guò)期時(shí)間后,會(huì)使用單獨(dú)的線程去構(gòu)建緩存。
整個(gè)過(guò)程如圖所示:
此方法有效杜絕了熱點(diǎn)key產(chǎn)生的問(wèn)題,但唯一不足的就是重構(gòu)緩存期間,會(huì)出現(xiàn)數(shù)據(jù)不一致的情況,這取決于應(yīng)用方是否容忍這種不一致。
兩種方案對(duì)比
到此這篇關(guān)于Redis作為緩存應(yīng)用的情形詳細(xì)分析的文章就介紹到這了,更多相關(guān)Redis作為緩存內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
java實(shí)現(xiàn)大文件導(dǎo)出的實(shí)現(xiàn)與優(yōu)化
這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)大文件導(dǎo)出的實(shí)現(xiàn)與優(yōu)化的相關(guān)資料,文中的示例代碼講解詳細(xì),對(duì)我們深入了解java有一定的幫助,感興趣的小伙伴可以了解下2023-11-11Java 實(shí)戰(zhàn)范例之線上婚紗攝影預(yù)定系統(tǒng)的實(shí)現(xiàn)
讀萬(wàn)卷書不如行萬(wàn)里路,只學(xué)書上的理論是遠(yuǎn)遠(yuǎn)不夠的,只有在實(shí)戰(zhàn)中才能獲得能力的提升,本篇文章手把手帶你用java+javaweb+SSM+springboot+mysql實(shí)現(xiàn)一個(gè)線上婚紗攝影預(yù)定系統(tǒng),大家可以在過(guò)程中查缺補(bǔ)漏,提升水平2021-11-11解決JMap抓取heap使用統(tǒng)計(jì)信息報(bào)錯(cuò)的問(wèn)題
這篇文章主要介紹了解決JMap抓取heap使用統(tǒng)計(jì)信息報(bào)錯(cuò)的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-12-12mybatis-plus中l(wèi)ambdaQuery()與lambdaUpdate()比較常見(jiàn)的使用方法總結(jié)
mybatis-plus是在mybatis的基礎(chǔ)上做增強(qiáng)不做改變,簡(jiǎn)化了CRUD操作,下面這篇文章主要給大家介紹了關(guān)于mybatis-plus中l(wèi)ambdaQuery()與lambdaUpdate()比較常見(jiàn)的使用方法,需要的朋友可以參考下2022-09-09java對(duì)象數(shù)組實(shí)現(xiàn)學(xué)生信息管理系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了java對(duì)象數(shù)組實(shí)現(xiàn)學(xué)生信息管理系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-06-06IDEA創(chuàng)建Maven項(xiàng)目后報(bào)錯(cuò)不出現(xiàn)src文件夾的情況解決
最近剛開(kāi)始學(xué)習(xí)maven,正準(zhǔn)備使用idea創(chuàng)建一個(gè)maven項(xiàng)目練手,卻發(fā)現(xiàn)自己創(chuàng)建的maven項(xiàng)目始終沒(méi)有src目錄,下面這篇文章主要給大家介紹了關(guān)于IDEA創(chuàng)建Maven項(xiàng)目后報(bào)錯(cuò)不出現(xiàn)src文件夾的情況解決,需要的朋友可以參考下2023-05-05java代碼實(shí)現(xiàn)MD5加密及驗(yàn)證過(guò)程詳解
這篇文章主要介紹了java代碼實(shí)現(xiàn)MD5加密及驗(yàn)證過(guò)程詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-10-10