日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網為廣大站長提供免費收錄網站服務,提交前請做好本站友鏈:【 網站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(50元/站),

點擊這里在線咨詢客服
新站提交
  • 網站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會員:747

對于緩存,大家肯定都不陌生,不管是前端還是服務端開發,緩存幾乎都是必不可少的優化方式之一。在實際生產環境中,緩存的使用規范也是一直備受重視的,如果使用的不好,很容易就遇到緩存擊穿、雪崩等嚴重異常情景,從而給系統帶來難以預料的災害。

為了避免緩存使用不當帶來的損失,我們有必要了解每種異常產生的原因和解決辦法,從而做出更好的預防措施。

緩存穿透

而緩存穿透是指緩存和數據庫中都沒有的數據,這樣每次請求都會去查庫,不會查緩存,如果同一時間有大量請求進來的話,就會給數據庫造成巨大的查詢壓力,甚至擊垮db系統。

緩存穿透、擊穿、雪崩什么的分不清楚?看了這篇文章后,我明白了

 

比如說查詢id為-1的商品,這樣的id在商品表里肯定不存在,如果沒做特殊處理的話,攻擊者很容易可以讓系統崩潰,那我們該如何避免這種情況發生呢?

一般來說,緩存穿透常用的解決方案大概有兩種:

一、緩存空對象

當緩存和數據都查不到對應key的數據時,可以將返回的空對象寫到緩存中,這樣下次請求該key時直接從緩存中查詢返回空對象,就不用走db了。當然,為了避免存儲過多空對象,通常會給空對象設置一個比較短的過期時間,就比如像這樣給key設置30秒的過期時間:

redisTemplate.opsForValue().set(key, null, 30, TimeUnit.SECONDS);

這種方法會存在兩個問題:

  • 如果有大量的key穿透,緩存空對象會占用寶貴的內存空間。
  • 空對象的key設置了過期時間,這段時間內可能數據庫剛好有了該key的數據,從而導致數據不一致的情況。

這種情況下,我們可以用更好的解決方案,也就是布隆過濾器

二、Bloom Filter

布隆過濾器(Bloom Filter)是1970年由一個叫布隆的小伙子提出的,是一種由一個很長的二進制向量和一系列隨機映射函數構成的概率型數據結構,這種數據結構的空間效率非常高,可以用于檢索集合中是否存在特定的元素。

設計思想

布隆過濾器由一個長度為m比特的位數組(bit array)與k個哈希函數(hash function)組成的數據結構。原理是當一個元素被加入集合時,通過K個散列函數將這個元素映射成一個位數組中的K個點,把它們置為1。檢索時,我們只要看看這些點是不是都是1就大約知道集合中有沒有它了,也就是說,如果這些點有任何一個0,則被檢元素一定不在;如果都是1,則被檢元素很可能在。

至于說為什么都是1的情況只是可能存在檢索元素,這是因為不同的元素計算的哈希值有可能一樣,會出現哈希碰撞,導致一個不存在的元素有可能對應的比特位為1。

舉個例子:下圖是一個布隆過濾器,共有18個比特位,3個哈希函數。當查詢某個元素w時,通過三個哈希函數計算,發現有一個比特位的值為0,可以肯定認為該元素不在集合中。

緩存穿透、擊穿、雪崩什么的分不清楚?看了這篇文章后,我明白了

 

優缺點

優點:

  • 節省空間:不需要存儲數據本身,只需要存儲數據對應hash比特位
  • 時間復雜度低:基于哈希算法來查找元素,插入和查找的時間復雜度都為O(k),k為哈希函數的個數

缺點:

  • 準確率有誤:布隆過濾器判斷存在,可能出現元素不在集合中;判斷準確率取決于哈希函數的個數
  • 不能刪除元素:如果一個元素被刪除,但是卻不能從布隆過濾器中刪除,這樣進一步導致了不存在的元素也會顯示1的情況。

適用場景

  • 爬蟲系統url去重
  • 垃圾郵件過濾
  • 黑名單

緩存擊穿

緩存擊穿從字面上看很容易讓人跟穿透搞混,這也是很多面試官喜歡埋坑的地方,當然,只要我們對知識點了然于心的話,面試的時候也不會那么被糊弄

簡單來說,緩存擊穿是指一個key非常熱點,在不停的扛著大并發,大并發集中對這一個點進行訪問,當這個key在失效的瞬間,持續的大并發就穿破緩存,直接請求數據庫,就好像堤壩突然破了一個口,大量洪水洶涌而入。

當發生緩存擊穿的時候,數據庫的查詢壓力會倍增,導致大量的請求阻塞。

解決辦法也不難,既然是熱點key,那么說明該key會一直被訪問,既然如此,我們就不對這個key設置失效時間了,如果數據需要更新的話,我們可以后臺開啟一個異步線程,發現過期的key直接重寫緩存即可。

當然,這種解決方案只適用于不要求數據嚴格一致性的情況,因為當后臺線程在構建緩存的時候,其他的線程很有可能也在讀取數據,這樣就會訪問到舊數據了。

如果要嚴格保證數據一致的話,可以用互斥鎖

互斥鎖

互斥鎖就是說,當key失效的時候,讓一個線程讀取數據并構建到緩存中,其他線程就先等待,直到緩存構建完后重新讀取緩存即可。

如果是單機系統,用JDK本身的同步工具Synchronized或ReentrantLock就可以實現,但一般來說,都達到防止緩存擊穿的流量了誰還搞什么單機系統,肯定是分布式高大上點啊,這種情況我們就可以用分布式鎖來做互斥效果。

為了你們能更懂流程,作為暖男的我還是一如既往的給你們準備了偽代碼啦:

public String getData(String key){
    String data = redisTemplate.opsForValue().get(key);
    if (StringUtils.isNotEmpty(data)){
        return data;
    }
    String lockKey = this.getClass().getName() + ":" + key;
    RLock lock = redissonClient.getLock(lockKey);
    try {
        boolean boo = lock.tryLock(5, 5, TimeUnit.SECONDS);
        if (!boo) {
            // 休眠一會兒,然后再請求
            Thread.sleep(200L);
            data = getData(key);
        }
        // 讀取數據庫的數據
        data = getDataByDB(key);
        if (StringUtils.isNotEmpty(data)){
            // 把數據構建到緩存中
            setDataToRedis(key,data);
        }
    } catch (InterruptedException e) {
        // 異常處理,記錄日志或者拋異常什么的
    }finally {
        if (lock != null && lock.isLocked()){
            lock.unlock();
        }
    }
    return data;
}

當然,采用互斥鎖的方案也是有缺陷的,當緩存失效的時候,同一時間只有一個線程讀數據庫然后回寫緩存,其他線程都處于阻塞狀態。如果是高并發場景,大量線程阻塞勢必會降低吞吐量。這種情況該如何處理呢?我只能說沒什么設計是完美的,你又想數據一致,又想保證吞吐量,哪有那么好的事,為了系統能更加健全,必要的時候犧牲下性能也是可以采取的措施,兩者之間怎么取舍要根據實際業務場景來決定,萬能的技術方案什么的根本不存在。

緩存雪崩

緩存雪崩也是key失效后大量請求打到數據庫的異常情況,不過,跟緩存擊穿不同的是,緩存擊穿因為指一個熱點key失效導致的情況,而緩存雪崩是指緩存中大批量的數據同時過期,巨大的請求量直接落到db層,引起db壓力過大甚至宕機,這也符合字面上的“雪崩”說法。

緩存穿透、擊穿、雪崩什么的分不清楚?看了這篇文章后,我明白了

 

解決方案

緩存雪崩的解決方案和擊穿的思路一致,可以設置key不過期或者互斥鎖的方式。

除此之外,因為是預防大面積的key同時失效,可以給不同的key過期時間加上隨機值,讓緩存失效的時間點盡量均勻 ,這樣可以保證數據不會在同一時間大面積失效

redisTemplate.opsForValue().set(Key, value, time + Math.random() * 1000, TimeUnit.SECONDS);  

同時還可以結合主備緩存策略來讓互斥鎖的方式更加的可靠,

主緩存:有效期按照經驗值設置,設置為主讀取的緩存,主緩存失效后從數據庫加載最新值。

備份緩存:有效期長,獲取鎖失敗時讀取的緩存,主緩存更新時需要同步更新備份緩存。

一般來說,上面三種緩存異常場景問的比較多,了解這幾種基本就夠了,但有些面試官可能喜歡劍走偏鋒,進一步延伸其他的異常情景做詢問,以防萬一,我們也加個菜,介紹下另外兩種常見緩存異常。

緩存預熱

緩存預熱就是系統上線后,先將相關的數據構建到緩存中,這樣就可以避免用戶請求的時候直接查庫。

這部分預熱的數據主要取決于訪問量和數據量大小,如果數據的訪問量不大的話,那么就沒必要做預熱,都沒什么多少請求了,直接按正常的緩存讀取流程執行就好。

訪問量大的話,也要看數據的大小來做預熱措施。

  • 數據量不大的時候,工程啟動的時候進行加載緩存動作,這種數據一般可以是電商首頁的運營位之類的信息;
  • 數據量大的時候,設置一個定時任務腳本,進行緩存的刷新;
  • 數據量太大的時候,優先保證熱點數據進行提前加載到緩存,并且確保訪問期間不能更改緩存,比如用定時器在秒殺活動前30分鐘就把商品信息之類的刷新到緩存,同時規定后臺運營人員不能在秒殺期間更改商品屬性。

緩存降級

緩存降級是指緩存失效或緩存服務器掛掉的情況下,不去訪問數據庫,直接返回默認數據或訪問服務的內存數據。

在項目實戰中通常會將部分熱點數據緩存到服務的內存中,類似HashMap、Guava這樣的工具,一旦緩存出現異常,可以直接使用服務的內存數據,從而避免數據庫遭受巨大壓力。

當然,這樣的操作對于業務是有損害的,分布式系統中很容易就出現數據不一致的問題,所以,一般這種情況下,我們都優先保證從運維角度確保緩存服務器的高可用性,比如Redis的部署采用集群方式,同時做好備份,總之,盡量避免出現降級的影響。

最后

關于緩存的幾大異常處理我們就講解到這了,雖然每種異常我們都給出了解決的方案,但不是說這玩意直接套上就能用了。現實開發過程中還是要根據實際情況來針對緩存做相應措施,比如用布隆過濾器預防緩存穿透雖然很有效,但并不算特別常用,這年頭,防止惡意攻擊什么的都是先在運維層面做限制,業務代碼層面更多的是對參數和數據做校驗。

如果每個使用緩存的地方都要考慮的這么復雜的話,那工作量無疑會更加繁雜,過度設計只會讓代碼維護起來也麻煩,而且實用性還不一定強,沒必要啊。程序員嘛,給自己增添煩惱的事情越少越好,畢竟我們最大的敵人不是996,而是那珍貴的發量啊。

分享到:
標簽:緩存
用戶無頭像

網友整理

注冊時間:

網站:5 個   小程序:0 個  文章:12 篇

  • 51998

    網站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會員

趕快注冊賬號,推廣您的網站吧!
最新入駐小程序

數獨大挑戰2018-06-03

數獨一種數學游戲,玩家需要根據9

答題星2018-06-03

您可以通過答題星輕松地創建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學四六

運動步數有氧達人2018-06-03

記錄運動步數,積累氧氣值。還可偷

每日養生app2018-06-03

每日養生,天天健康

體育訓練成績評定2018-06-03

通用課目體育訓練成績評定