緩存世界中的三大問題及解決方案

目前的IO設備遠不能知足互聯網應用海量的讀寫請求。因而便出現了緩存,利用內存的高速讀寫性能來應付海量的查詢請求。然而內存資源很是寶貴,將全量數據存儲在內存中顯然是不切合實際的。所以目前採用內存和IO結合的方式,內存只存儲熱點數據,而IO設備存儲全量數據。
緩存的設計包含不少技巧,設計不當將會致使嚴重的後果。本文將介紹緩存使用中常見的三大問題,並給出相應的解決方案。

關注訂閱個人文章

1. 緩存穿透

在大多數互聯網應用中,緩存的使用方式以下圖所示:
title程序員

  1. 當業務系統發起某一個查詢請求時,首先判斷緩存中是否有該數據;
  2. 若是緩存中存在,則直接返回數據;
  3. 若是緩存中不存在,則再查詢數據庫,而後返回數據。

瞭解了上述過程後,下面說說緩存穿透。數據庫

1.1 什麼是緩存穿透?

業務系統要查詢的數據根本就存在!當業務系統發起查詢時,按照上述流程,首先會前往緩存中查詢,因爲緩存中不存在,而後再前往數據庫中查詢。因爲該數據壓根就不存在,所以數據庫也返回空。這就是緩存穿透。segmentfault

綜上所述:業務系統訪問壓根就不存在的數據,就稱爲緩存穿透。緩存

1.2 緩存穿透的危害

若是存在海量請求查詢壓根就不存在的數據,那麼這些海量請求都會落到數據庫中,數據庫壓力劇增,可能會致使系統崩潰(你要知道,目前業務系統中最脆弱的就是IO,稍微來點壓力它就會崩潰,因此咱們要想種種辦法保護它)。分佈式

1.3 爲何會發生緩存穿透?

發生緩存穿透的緣由有不少,通常爲以下兩種:工具

  1. 惡意攻擊,故意營造大量不存在的數據請求咱們的服務,因爲緩存中並不存在這些數據,所以海量請求均落在數據庫中,從而可能會致使數據庫崩潰。
  2. 代碼邏輯錯誤。這是程序員的鍋,沒啥好講的,開發中必定要避免!

1.4 緩存穿透的解決方案

下面來介紹兩種防止緩存穿透的手段。性能

1.4.1 緩存空數據

之因此發生緩存穿透,是由於緩存中沒有存儲這些空數據的key,致使這些請求全都打到數據庫上。spa

那麼,咱們能夠稍微修改一下業務系統的代碼,將數據庫查詢結果爲空的key也存儲在緩存中。當後續又出現該key的查詢請求時,緩存直接返回null,而無需查詢數據庫。設計

1.4.2 BloomFilter

第二種避免緩存穿透的方式即爲使用BloomFilter。blog

它須要在緩存以前再加一道屏障,裏面存儲目前數據庫中存在的全部key,以下圖所示:

title

當業務系統有查詢請求的時候,首先去BloomFilter中查詢該key是否存在。若不存在,則說明數據庫中也不存在該數據,所以緩存都不要查了,直接返回null。若存在,則繼續執行後續的流程,先前往緩存中查詢,緩存中沒有的話再前往數據庫中的查詢。

1.4.3 兩種方案的比較

這兩種方案都能解決緩存穿透的問題,但使用場景卻各不相同。

對於一些惡意攻擊,查詢的key每每各不相同,並且數據賊多。此時,第一種方案就顯得提襟見肘了。由於它須要存儲全部空數據的key,而這些惡意攻擊的key每每各不相同,並且同一個key每每只請求一次。所以即便緩存了這些空數據的key,因爲再也不使用第二次,所以也起不了保護數據庫的做用。
所以,對於空數據的key各不相同key重複請求機率低的場景而言,應該選擇第二種方案。而對於空數據的key數量有限key重複請求機率較高的場景而言,應該選擇第一種方案。

2. 緩存雪崩

2.1 什麼是緩存雪崩?

經過上文可知,緩存其實扮演了一個保護數據庫的角色。它幫數據庫抵擋大量的查詢請求,從而避免脆弱的數據庫受到傷害。

若是緩存因某種緣由發生了宕機,那麼本來被緩存抵擋的海量查詢請求就會像瘋狗同樣涌向數據庫。此時數據庫若是抵擋不了這巨大的壓力,它就會崩潰。

這就是緩存雪崩。

2.2 如何避免緩存雪崩?

2.2.1 使用緩存集羣,保證緩存高可用

也就是在雪崩發生以前,作好預防手段,防止雪崩的發生。
PS:關於分佈式高可用問題不是今天討論的重點,套路就那些,後面會有高可用的相關文章,盡請關注。

2.2.2 使用Hystrix

Hystrix是一款開源的「防雪崩工具」,它經過 熔斷、降級、限流三個手段來下降雪崩發生後的損失。

Hystrix就是一個Java類庫,它採用命令模式,每一項服務處理請求都有各自的處理器。全部的請求都要通過各自的處理器。處理器會記錄當前服務的請求失敗率。一旦發現當前服務的請求失敗率達到預設的值,Hystrix將會拒絕隨後該服務的全部請求,直接返回一個預設的結果。這就是所謂的「熔斷」。當通過一段時間後,Hystrix會放行該服務的一部分請求,再次統計它的請求失敗率。若是此時請求失敗率符合預設值,則徹底打開限流開關;若是請求失敗率仍然很高,那麼繼續拒絕該服務的全部請求。這就是所謂的「限流」。而Hystrix向那些被拒絕的請求直接返回一個預設結果,被稱爲「降級」

更多Hystrix的介紹請參閱:https://segmentfault.com/a/11...

3. 熱點數據集中失效

3.1 什麼是熱點數據集中失效?

咱們通常都會給緩存設定一個失效時間,過了失效時間後,該數據庫會被緩存直接刪除,從而必定程度上保證數據的實時性。

可是,對於一些請求量極高的熱點數據而言,一旦過了有效時間,此刻將會有大量請求落在數據庫上,從而可能會致使數據庫崩潰。其過程以下圖所示:

title

若是某一個熱點數據失效,那麼當再次有該數據的查詢請求[req-1]時就會前往數據庫查詢。可是,從請求發往數據庫,到該數據更新到緩存中的這段時間中,因爲緩存中仍然沒有該數據,所以這段時間內到達的查詢請求都會落到數據庫上,這將會對數據庫形成巨大的壓力。此外,當這些請求查詢完成後,都會重複更新緩存。

3.2 解決方案

3.2.1 互斥鎖

咱們可使用緩存自帶的鎖機制,當第一個數據庫查詢請求發起後,就將緩存中該數據上鎖;此時到達緩存的其餘查詢請求將沒法查詢該字段,從而被阻塞等待;當第一個請求完成數據庫查詢,並將數據更新值緩存後,釋放鎖;此時其餘被阻塞的查詢請求將能夠直接從緩存中查到該數據。

當某一個熱點數據失效後,只有第一個數據庫查詢請求發往數據庫,其他全部的查詢請求均被阻塞,從而保護了數據庫。可是,因爲採用了互斥鎖,其餘請求將會阻塞等待,此時系統的吞吐量將會降低。這須要結合實際的業務考慮是否容許這麼作。

互斥鎖能夠避免某一個熱點數據失效致使數據庫崩潰的問題,而在實際業務中,每每會存在一批熱點數據同時失效的場景。那麼,對於這種場景該如何防止數據庫過載呢?

3.3.2 設置不一樣的失效時間

當咱們向緩存中存儲這些數據的時候,能夠將他們的緩存失效時間錯開。這樣可以避免同時失效。如:在一個基礎時間上加/減一個隨機數,從而將這些緩存的失效時間錯開。

關注訂閱個人文章

相關文章
相關標籤/搜索