NO IMAGE

點選上方藍字進行關注的都是靚仔和仙女

      對後端開發的同學來說,快取是必備技能。這是你不需要花費太多的精力就能顯著提升服務效能的靈丹妙藥。前提是你得知道如何使用它,這樣才能夠最大限度發揮它的功效,並抑制其副作用。本文將介紹最如何正確的新增和更新快取


寫在開始之前

      這部分將介紹在開始加快取之前我們必須要做的事情。這步非常重要,如果沒弄好,很有可能加了快取反而不如不加。

      為什麼要用快取?對於一個服務其效能瓶頸往往都在DB,傳統關係型儲存尤甚。我們在建立表的時候,並不會未所有的欄位建立索引,這意味著如果我們需要讀取非快取資料就要從磁碟拿資料。這個過程至少需要十幾毫秒的時間。而快取往往是基於記憶體的,這要比DB讀資料快兩個數量級。這是我們用快取的根本原因原因。

     
那乾脆把所有的資料扔到記憶體不就行了嘛!不行。
記憶體這東西雖然很快,同時它還很貴。動輒百十來G的資料都扔記憶體這有點太浪費。依據二八定律,我們只需找到那最緊俏的百分之二十就行了。這是非常重要的。否則你加了快取效果反而更差。

      對於快取有一個衡量指標,叫做快取命中率。這個指標高說明我們請求的資料大部分來自快取。證明我們加快取這件事的收益越高。

加快取

      如果你平時都用一些ORM工具很可能下邊這些問題你不會直接遇到,不過這些問題都是在你加快取之前需要著實想清楚的。算是一些通用的套路。我們逐條來看一下。

快取穿透

      快取穿透是說訪問一個快取中沒有的資料,但是這個資料資料庫中也不存在。普通思路下我們沒有從資料庫中拿到資料是不會觸發加快取操作的。這時如果是有人惡意攻擊,大量的訪問就會透過快取直接打到資料庫,對後端服務和資料庫做成巨大的壓力甚至宕機。

解決方案:

  • 快取空物件如果快取未命中,而資料庫中也沒有這個物件,則可以快取一個空物件到快取。如果使用Redis,這種key需設定一個較短的時間,以防記憶體浪費。

  • 快取預測預測key是否存在。如果快取的量不大可以使用hash來判斷,如果量大可以使用布隆過濾器來做判斷。

快取併發

     
快取併發這個場景很容易解釋:
多個客戶端同時訪問一個沒有在cache中的資料,這時每個客戶端都會執行從DB載入資料set到快取,就會造成快取併發。

解決方案:

  • 快取預熱提前把所有預期的熱資料加到快取。定位熱資料還是比較複雜的事情,需要根據自己的服務訪問情況去評估。這個方案只能減輕快取併發的發生次數不能全部抵制。

  • 快取加鎖 如果多個客戶端訪問不存在的快取時,在執行載入資料並set快取這個邏輯之前先加鎖,只能讓一個客戶端執行這段邏輯。

快取防雪崩

      快取雪崩是快取服務暫時不能提供服務,導致所有的請求都直接訪問DB。

解決方案:

  • 構建高可用的快取系統。目前常用的快取系統Redis和Memcache都支援高可用的部署方式,所以部署的時候不防先考慮是否要以高可用的叢集方式部署。

  • 限流。Netflix的Hystrix是非常不錯的工具,在用快取時不妨搭配它來使用。

更新快取

      這部分我們將介紹一下cache的更新策略。這部分內容主要是來自CoolShell 左耳朵耗子老師,文末有原文地址,大家可以前去拜讀。

Cache Aside Pattern

640?wx_fmt=png

     
這種思路先更新資料庫,更新成功之後再令快取失效。還有一種方式是先失效快取,然後在更新資料庫我們來對比一下這兩種方式的不同。

      首先,來看後一種設想一種情景,一個客戶端發起更新操作,當執行了快取失效。這時一個讀取操作進來,發現快取沒有資料然後從資料庫拿資料並放到快取。更新操作繼續更新資料庫。這時快取裡已經快取了髒資料。

      那麼第一種會出現這種問題嗎?理論上是會的,看一下這個操作:A客戶端發起更新操作,B客戶端發起讀操作,並且這時快取恰好失效,然後它從資料庫載入資料(老資料)。A的更新操作完成失效快取,這時B讀取的客戶端把老資料set到快取。這有這種情況下才會出現髒資料,但是這概率已經非常小了。

Read/Write Through Pattern

640?wx_fmt=png

  • Read Through讀取資料的時候如果當前快取中沒有資料,慣常的操作都是應用程式去DB載入資料,然後加入到快取中。Read Through與之不同的是我們不需要在應用程式自己載入資料了,快取層會幫忙做件事。

  • Write Through:更新資料的時候,如果命中快取,則先更新快取然後快取在負責把資料更新到資料庫;如果沒有命中快取則直接更新資料庫。

      這種方式快取層直接遮蔽了DB,應用程式只需要更快取打交道。優點是應用邏輯簡單了,而且更高效了;缺點是快取層的實現相對複雜一些。

Write Back Pattern

640?wx_fmt=png

     
這是三種中實現難度最大的一種方式,它需要一個專門的儲存儲存快取是否是髒資料,並在讀寫快取時同步髒資料。在資料一致性要求不太高的場景可以使用這種方式。

      首先我們來看一下讀快取的操作。如果快取命中直接返回。如果快取沒有命中,則首先去strore中檢索這個key是否dirty,如果不是則載入資料,如果是應先把資料flush到儲存,然後在載入資料。接下來標記這條key為not dirty,返回結果。

     
寫資料的過程。
如果命中快取則更新資料,並標記這條記錄為dirty。如果沒有命中,則去store中檢索這個可以是否dirty,如果不是則從儲存load資料,更新這條資料,如果是則把當前資料flush到儲存,然後load資料更新,並標記這條記錄為dirty。

0?wx_fmt=gif

好了今天的快取就說到這了

因為…

好戲還在後頭呢…

在這裡…

部落依舊會為大家推薦

一堂java課程

而今天推薦的這堂課..

將是由我們的Lison大神…

給大家帶來的

《網際網路高效能開發必備技能 快取技術架構解析》

你只需要

在今晚的8:30分

點選文章最下方閱讀原文

即可觀看今晚

在騰訊課堂播出的

動腦學院

Java高階免費試聽課程

0?wx_fmt=png

推薦閱讀

高併發與分散式系統的基石–資料庫讀寫分離實戰

這就是學程式設計的下場…

論程式設計師與產品經理是怎麼互掐起來的

如何假裝成為一名好的程式設計師

2017年長沙網際網路線上技術峰會

來自部落的邀請

Java框架 Spring 核心機制

至程式設計師的情書

Java高階部落送你ofo小黃車60天免費騎行,還不來?

Facebook研發的Cassandra你用過嗎?

給 Java開發者的10個大資料工具和框架

推薦程式設計師必備微訊號