欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Redis作為緩存應(yīng)用的情形詳細(xì)分析

 更新時(shí)間:2023年01月18日 15:17:21   作者:氵奄不死的魚  
實(shí)際開(kāi)發(fā)中緩存處理是必須的,不可能我們每次客戶端去請(qǐng)求一次服務(wù)器,服務(wù)器每次都要去數(shù)據(jù)庫(kù)中進(jìn)行查找,為什么要使用緩存?說(shuō)到底是為了提高系統(tǒng)的運(yùn)行速度

為什么使用緩存

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)文章

最新評(píng)論