歡迎光臨
每天分享高質量文章

快取穿透、快取併發、快取失效之思路變遷

點擊上方“芋道原始碼”,選擇“置頂公眾號”

技術文章第一時間送達!

原始碼精品專欄

 

來源:https://www.jianshu.com/p/d96906140199

我們在用快取的時候,不管是Redis或者Memcached,基本上會通用遇到以下三個問題:

  • 快取穿透

  • 快取併發

  • 快取失效

一、快取穿透

註:
上面三個圖會有什麼問題呢?

我們在專案中使用快取通常都是先檢查快取中是否存在,如果存在直接傳回快取內容,如果不存在就直接查詢資料庫然後再快取查詢結果傳回。這個時候如果我們查詢的某一個資料在快取中一直不存在,就會造成每一次請求都查詢DB,這樣快取就失去了意義,在流量大時,可能DB就掛掉了。

那這種問題有什麼好辦法解決呢?

要是有人利用不存在的key頻繁攻擊我們的應用,這就是漏洞。
有一個比較巧妙的作法是,可以將這個不存在的key預先設定一個值。
比如,”key” , “&&”。
在傳回這個&&值的時候,我們的應用就可以認為這是不存在的key,那我們的應用就可以決定是否繼續等待繼續訪問,還是放棄掉這次操作。如果繼續等待訪問,過一個時間輪詢點後,再次請求這個key,如果取到的值不再是&&,則可以認為這時候key有值了,從而避免了透傳到資料庫,從而把大量的類似請求擋在了快取之中。

二、快取併發

有時候如果網站併發訪問高,一個快取如果失效,可能出現多個行程同時查詢DB,同時設置快取的情況,如果併發確實很大,這也可能造成DB壓力過大,還有快取頻繁更新的問題。

我現在的想法是對快取查詢加鎖,如果KEY不存在,就加鎖,然後查DB入快取,然後解鎖;其他行程如果發現有鎖就等待,然後等解鎖後傳回資料或者進入DB查詢。

這種情況和剛纔說的預先設定值問題有些類似,只不過利用鎖的方式,會造成部分請求等待。

三、快取失效

引起這個問題的主要原因還是高併發的時候,平時我們設定一個快取的過期時間時,可能有一些會設置1分鐘啊,5分鐘這些,併發很高時可能會出在某一個時間同時生成了很多的快取,並且過期時間都一樣,這個時候就可能引發一當過期時間到後,這些快取同時失效,請求全部轉發到DB,DB可能會壓力過重。

那如何解決這些問題呢?
其中的一個簡單方案就時講快取失效時間分散開,比如我們可以在原有的失效時間基礎上增加一個隨機值,比如1-5分鐘隨機,這樣每一個快取的過期時間的重覆率就會降低,就很難引發集體失效的事件。

我們討論的第二個問題時針對同一個快取,第三個問題時針對很多快取。

總結來看:

1、快取穿透:查詢一個必然不存在的資料。比如文章表,查詢一個不存在的id,每次都會訪問DB,如果有人惡意破壞,很可能直接對DB造成影響。

2、快取失效:如果快取集中在一段時間內失效,DB的壓力凸顯。這個沒有完美解決辦法,但可以分析用戶行為,儘量讓失效時間點均勻分佈。
當發生大量的快取穿透,例如對某個失效的快取的大併發訪問就造成了快取雪崩。

四、大家提問彙總

1、問題1:
如何解決DB和快取一致性問題?
答:當修改了資料庫後,有沒有及時修改快取。這種問題,以前有過實踐,修改資料庫成功,而修改快取失敗的情況,最主要就是快取服務器掛了。而因為網絡問題引起的沒有及時更新,可以通過重試機制來解決。而快取服務器掛了,請求首先自然也就無法到達,從而直接訪問到資料庫。那麼我們在修改資料庫後,無法修改快取,這時候可以將這條資料放到資料庫中,同時啟動一個異步任務定時去檢測快取服務器是否連接成功,一旦連接成功則從資料庫中按順序取出修改資料,依次進行快取最新值的修改。

2、問題2:
問下快取穿透那塊!例如,一個用戶查詢文章,通過ID查詢,按照之前說的,是將快取的KEY預先設置一個值,,如果通過ID插過來,發現是預先設定的一個值,比如說是“&&”,那之後的繼續等待訪問是什麼意思,這個ID什麼時候會真正被附上用戶所需要的值呢?
答:我剛說的主要是咱們常用的後面配置,前臺獲取的場景。前臺無法獲取相應的key,則等待,或者放棄。當在後臺配置界面上配置了相關key和value之後,那麼以前的key &&也自然會被替換掉。你說的那種情況,自然也應該會有一個行程會在某一個時刻,在快取中設置這個ID,再有新的請求到達的時候,就會獲取到最新的ID和value。

3、問題3:
其實用redis的話,那天看到一個不錯的例子,雙key,有一個當時生成的一個附屬key來標識資料修改到期時間,然後快到的時候去重新加載資料,如果覺得key多可以把結束時間放到主key中,附屬key起到鎖的功能。
答:這種方案,之前我們實踐過。這種方案會產生雙份資料,而且需要同時控制附屬key與key之間的關係,操作上有一定複雜度。

4、問題4:
多級快取是什麼概念呢?
答:多級快取就像我今天之前給大家發的文章裡面提到了,將ehcache與redis做二級快取,就像我之前寫的文章 http://www.jianshu.com/p/2cd6ad416a5a 提到過的。但同樣會存在一致性問題,如果我們需要強一致性的話,快取與資料庫同步是會存在時間差的,所以我們在具體開發的過程中,一定要根據場景來具體分析,二級快取更多的解決是,快取穿透與程式的健壯性,當集中式快取出現問題的時候,我們的應用能夠繼續運行。



如果你對 Dubbo / Netty 等等原始碼與原理感興趣,歡迎加入我的知識星球一起交流。長按下方二維碼噢

目前在知識星球更新了《Dubbo 原始碼解析》目錄如下:

01. 除錯環境搭建
02. 專案結構一覽
03. 配置 Configuration
04. 核心流程一覽

05. 拓展機制 SPI

06. 執行緒池

07. 服務暴露 Export

08. 服務取用 Refer

09. 註冊中心 Registry

10. 動態編譯 Compile

11. 動態代理 Proxy

12. 服務呼叫 Invoke

13. 呼叫特性 

14. 過濾器 Filter

15. NIO 服務器

16. P2P 服務器

17. HTTP 服務器

18. 序列化 Serialization

19. 集群容錯 Cluster

20. 優雅停機

21. 日誌適配

22. 狀態檢查

23. 監控中心 Monitor

24. 管理中心 Admin

25. 運維命令 QOS

26. 鏈路追蹤 Tracing

… 一共 69+ 篇

目前在知識星球更新了《Netty 原始碼解析》目錄如下:

01. 除錯環境搭建
02. NIO 基礎
03. Netty 簡介
04. 啟動 Bootstrap

05. 事件輪詢 EventLoop

06. 通道管道 ChannelPipeline

07. 通道 Channel

08. 位元組緩衝區 ByteBuf

09. 通道處理器 ChannelHandler

10. 編解碼 Codec

11. 工具類 Util

… 一共 61+ 篇

目前在知識星球更新了《資料庫物體設計》目錄如下:


01. 商品模塊
02. 交易模塊
03. 營銷模塊
04. 公用模塊

… 一共 17+ 篇


目前在知識星球更新了《Spring 原始碼解析》目錄如下:


01. 除錯環境搭建
02. IoC Resource 定位
03. IoC BeanDefinition 載入

04. IoC BeanDefinition 註冊

05. IoC Bean 獲取

06. IoC Bean 生命周期

… 一共 35+ 篇


原始碼不易↓↓↓

點贊支持老艿艿↓↓

赞(0)

分享創造快樂