1. 程式人生 > >效能優化之快取篇

效能優化之快取篇

1. 前言:為什麼要用快取?

使用者數增長,架構演變,資料量增大,開始考慮怎麼去做效能優化。

而效能優化的第一定律就是:優先考慮使用快取。

2. 快取的基本原理

2.1 快取的作用

1、加快資料訪問速度;

2、減輕後端應用和資料儲存的負載壓力。

2.2 快取的特徵

1、命中率:命中率 = 命中數 / 請求數。

這是衡量快取有效性的重要指標。命中率越高,表明快取的使用率越高。

2、最大元素(最大空間)。

一旦快取中元素數量超過這個值(或者快取資料空間超過其最大支 持空間),將會觸發淘汰策略

3、淘汰策略。

這個我前文其實已經說過。

FIFO(First In First Out) 先進先出,淘汰最早資料。

判斷儲存時間,離目前最遠的資料優先淘汰。

LRU (Least Recently Used)剔除最近最少使用。

判斷最近使用時間,離目前最遠的資料優先淘汰。

LFU (Least Frequently Used)剔除最近使用頻率最低的資料。

在一段時間內,資料被使用次數最少的,優先淘汰。

具體可以看這篇文章常見的快取剔除策略 & LRU與LFU的區別。

3. 快取的分類

快取的主要手段有:瀏覽器快取、CDN、反向代理、本地快取、分散式快取、資料庫快取。

在解讀《大型網站技術架構》一文中,其實已經說到過。

我們一般說做效能優化時是指後三個:本地快取、分散式快取、資料庫快取。

前面三個快取策略屬於網站前端的範疇。

從硬體介質上來看,快取分為記憶體和硬碟兩種。

但從技術上,又可以分成記憶體、硬碟檔案、資料庫。

我們通常意義上說的快取一般都是基於記憶體的。

因為只有記憶體,才足夠快。

資料庫快取一般也是基於記憶體的,但這個活一般是DBA在配置資料庫的時候就設定好了。

對於大部分開發人員來說,我們一般所說的快取優化都是基於本地快取(ocal cache)和遠端快取(remote cache)。

而現在遠端快取這個詞一般也被分散式快取這個常用方案所代指。

4. 什麼時候使用快取?

4.1 快取的使用判斷

什麼時候使用快取的判斷其實比較簡單,抓住兩點就行了:

1、是不是熱點資料?

所謂熱點,一般是遵循二八定律,即百分之八十的訪問集中在百分之二十的資料上。

2、是不是讀比寫多?

這個比例一般為2:1。

4.2 什麼時候不應該使用快取?

反過來就是了。

1、沒有熱點資料不要使用快取,也沒什麼意義。

因為記憶體資源是比較寶貴的。

2、頻繁修改的資料不要使用快取。

因為可能寫入後還來不及讀取就已失效或被淘汰,並且容易產生髒讀。

4.3 合理使用快取

最後,最重要的是確認是否需要使用快取?

確定了後,再選擇合適的快取工具及使用快取的方式。

5. 快取時常見的一些問題

使用快取優點很多,但也存在一些很常見的問題。雙刃之劍,就看怎麼用了。

列舉一些我們工作中常見的一些快取問題,並給出至少一種解決方案。

5.1 快取更新帶來的資料不一致與髒讀

快取更新的常見策略有:

1、先更新資料庫再更新快取;

2、先更新資料庫再刪除快取;

3、先刪除快取再更新資料庫;

4、定時清理快取;

5、有請求訪問資料時,判斷快取是否過期,過期從資料庫中重新整理快取。

在這幾種方案中,如果修改快取與資料庫不在同一個事物中,就帶來了資料不一致和髒讀的問題。

對應方案1:先刪除快取再更新資料庫,並且在同一個事物中。

對應方案2:快取自動失效後,另外的非同步執行緒進行快取更新。

對應方案3:快取更新在併發、分散式要考慮鎖,redis天生就是單執行緒,比較有優勢。

5.2 怎麼做快取預熱

快取預熱是指在使用者可訪問服務之前,將熱點資料載入到快取的操作,這樣可以有效避免上線後瞬時大流量造成系統不可用。

快取預熱的一般性策略:

1、開發個快取重新整理功能,手工重新整理;

2、專案啟動的時候自動進行載入(一般為字典表等資料量不大的資料);

3、設定個定時器,自動重新整理快取;

4、提前統計熱點資料,事先批量載入到如redis這樣快取工具中。

5.3 快取重建

快取失效後,重建熱點快取,如果耗時較長,在重建過程中,效能、負載不好。

對應方案:

1、正常情況下,交錯快取失效時間,減輕快取壓力;

2、崩潰失效的情況下,可以使用帶持久化功能的快取來恢復,比如Redis;

3、如果是MongoDB則不太一樣,它是採用mmap來將資料檔案對映到記憶體中,所以當MongoDB重啟時,這些對映的記憶體並不會清掉,不需要進行快取重建與預熱。

5.4 快取雪崩與可用性

快取雪崩:快取在同一時間失效時,訪問直達資料庫層,可能導致DB掛掉、系統崩潰。

對應方案1:交錯快取失效時間或隨機快取失效時間。

對應方案2:主從熱備(Redis Sentinel)。

對應方案3:叢集/水平切分(Redis Cluster、一致性雜湊)。

5.5 快取穿透

快取穿透:持續高併發訪問某個不存在的Key。

對應方案1:空值快取。

對應方案2:布隆過濾器(bloom filter) + bitmap。窮舉可能訪問的資料放入bitmap中,使用hash訪問。

5.6 快取擊穿

快取擊穿:熱點Key失效,高併發請求,直擊資料庫。

快取擊穿與快取穿透很相似,不同點是是快取擊穿前訪問的是真實的熱點資料,只是在某一剎那失效了,造成了擊穿的效果。

這樣看,它其實也是快取雪崩的一個特例。與雪崩的區別即在於擊穿是對於特定的熱點資料,而雪崩是全部資料。

對應方案:多級快取及交錯失效時間 + LRU 淘汰演算法。

對於熱點資料進行二級或多級快取,並對於不同級別的快取設定不同的失效時間,緩解雪崩。

此外可使用LRU的變種演算法LRU-K快取資料。

5.7 快取降級

快取降級是服務降級中的一環。

在訪問量劇增,導致服務出現問題時,為了保證核心服務可用,防止發生快取雪崩,可進行服務降級。

以redis為例,比較常見的做法就是,不去資料庫查詢,而是直接返回預設值給使用者。

快取降級也可根據日誌級別進行預案設定。

6. 分散式快取的選型

說了這麼多快取的原理與策略,說說我們在實際工作中應該怎麼去做快取選型。

以下就是常用的幾種快取工具。

6.1 Ehcache

Ehcache是純Java開源的快取框架,最早從hibernate發展而來,現在算是springboot中的官配快取工具,整合簡單。特點如下:

  • 快速,針對大型高併發系統場景,Ehcache的多執行緒機制有相應的優化改善;

  • 簡單,很小的jar包,簡單配置就可直接使用,單機場景下無需過多的其他服務依賴;

  • 支援多種的快取策略,靈活;

  • 快取資料有兩級:記憶體和磁碟,與一般的本地記憶體快取相比,有了磁碟的儲存空間,將可以支援更大量的資料快取需求;

  • 具有快取和快取管理器的偵聽介面,能更簡單方便的進行快取例項的監控管理;

  • 支援多快取管理器例項,以及一個例項的多個快取區域。

6.2 Guava Cache

Guava Cache是Google開源的Java重用工具集庫Guava裡的一款快取工具,特點如下:

  • 自動將entry節點載入進快取結構中;

  • 當快取的資料超過設定的最大值時,使用LRU演算法移除;

  • 具備根據entry節點上次被訪問或者寫入時間計算它的過期機制;

  • 快取的key被封裝在WeakReference引用內;

  • 快取的Value被封裝在WeakReference或SoftReference引用內;

  • 統計快取使用過程中命中率、異常率、未命中率等統計資料。

6.3 Memcache

memcache本身不支援分散式,是通過客戶端的路由處理來達到分散式解決方案的目的。特點如下:

  • memcache使用預分配記憶體池的方式管理記憶體;

  • 所有資料儲存在實體記憶體裡;

  • 非阻塞IO複用模型,純KV存取操作;

  • 多執行緒,效率高,會遇到鎖等上下文切換問題;

  • 只支援簡單KV資料型別;

  • 資料不支援持久化。

6.4 Redis

Redis是當前主流的高效能記憶體資料庫,多用於儲存快取資料,並能實現輕量級的MQ功能。特點如下:

  • 臨時申請空間,可能導致碎片;

  • 有VM機制,能儲存更多資料,超過記憶體空間後會導致swap,降低效率;

  • 非阻塞IO複用模型,支援額外CPU計算:排序、聚合,會影響IO效能;

  • 單執行緒,無鎖,無上下文切換,單例項無法利用多核效能;

  • 支援多種資料型別:string / hash / list / set / sorted set;

  • 資料支援持久化:AOF(語句增量)/RDB(fork全量);

  • 天然支援高可用分散式方案sentinel +;

  • cluster(故障自動轉移+叢集)。

6.5 推薦

通常情況下,單機我們會用Ehcache,甚至java自己的concurrenthashmap來實現快取。

分散式一般選擇redis。

07 附錄:參考資料

快取那些事 | 美團明輝

什麼是快取預熱,快取更新,快取降級的概念?