極客大學架構師訓練營 系統架構 分佈式緩存 一致性哈希 Hash 第9課 聽課總結

說明

講師:李智慧

緩存 Cache

緩存:存儲在計算機上的一個原始數據複製集,以便於訪問。 – 維基百科

緩存是介於數據訪問者和數據源之間的一種高速存儲,當數據需要多次讀取的時候,用於加快讀取的速度。

緩存(Cache) 和 緩衝(Buffer) 的分別?
緩存:一般是爲了數據多次讀取。

緩衝:比如CPU寫到 把數據先硬盤,因爲硬盤比較慢,先到緩衝設備Buffer,不如內存,Buffer讀和寫都需要。

無處不在的緩存

  • CPU 緩存

  • 操作系統緩存

  • 數據庫緩存

  • JVM 編譯緩存

  • CDN 緩存

  • 代理與反向代理緩存

  • 前端緩存

  • 應用程序緩存

  • 分佈式對象緩存

緩存數據存儲(Hash表)

在這裏插入圖片描述
緩存數據存儲:一般數據放到內存中。
主要是存儲數據結構:Hash表
業界緩存數量:百萬級~億級

緩存的關鍵指標

緩存命中率

  • 緩存是否有效依賴於能多少次重用同一個緩存響應業務請求,這個度量指標被稱作緩存命中率。
  • 如果查詢一個緩存,十次查詢九次能夠得到正確結果,那麼它的命中率是 90%.

影響緩存命中率的主要指標

  • 緩存鍵集合大小
  • 緩存可使用內存空間
  • 緩存對象生存時間

緩存鍵集合大小

緩存中的每個對象使用緩存鍵進行識別,定位一個對象的唯一方式就是對緩存鍵執行精確匹配。例如,如果想爲每個商品緩存在線商品信息,你需要使用商品 ID 作爲緩存鍵。換句話說,緩存鍵空間是你的應用能夠生成的所有鍵的數量。從統計數字上看,應用生成的唯一鍵越多,重用的機會越小。例如,如果想基於客戶 IP 地址緩存天氣數據,則可能有多大 40 億個鍵(這是所有可能 IP 地址的數量)。如果要基於客戶來源國家緩存天氣數據,則可能僅需幾百個緩存鍵(世界上所有國家的數量)。一定要想辦法減少可能的緩存數量。鍵數量越少,緩存的效率越高。

緩存可使用內存空間

緩存可使用內存空間直接解決了緩存對象的平均大小和緩存對象數量。因爲緩存通常存儲在內存中,緩存對象可用空間受到嚴格限制且相對昂貴。如果想緩存更多的對象,就需要先刪除老的對象,再添加新的對象。清除對象會降低緩存命中率,因爲緩存對象被刪除後,將來的請求就無法命中了。物理上能緩存的對象越多,緩存命中率就越高。

緩存對象生存時間

緩存對象生存時間稱爲TTL(Time To Live)。在某些場景中,例如,緩存天氣預報數據 15 分鐘沒問題。在這個場景下,你可以設置緩存對象定義 TTL 爲 15 分鐘。在其它場景中,你可能不能冒險使用過於陳舊的數據。例如,在一個電子商務系統中,店鋪管理員可能在任何時刻修改商品價格,如果這些價格需要準確地展示在整個網站中。在這個場景下,你需要在每個商品價格修改是讓緩存失效。簡單講,對象緩存的時間越長,緩存對象被重用的可能性就越高。

代理緩存

在這裏插入圖片描述

反向代理緩存

在這裏插入圖片描述

多層反向代理緩存

在這裏插入圖片描述

內容分發網絡(CDN - Content Distribution Network)

在這裏插入圖片描述
CDN國內公司:ChinaCache在08年的時候,互聯網需求量很大的時候,ChinaCache差點垮掉。ChinaCache提前買了很多CDN服務器放到運營商(中國移動、電信)。但是優酷、土豆等互聯網公司快速發展,他們也選擇了自建,自己買CDN服務器放到運營商機房。

小網站:雲服務提供。
大網站:Alibaba、騰訊等都是自建CDN。

CDN 同時配置靜態文件和動態內容

在這裏插入圖片描述

通讀緩存(Read-Through)

  • 代理緩存,反向代理緩存,CDN 緩存都是通讀緩存。
  • 通讀緩存給客戶端返回緩存資源,並在請求未命中緩存時獲取實際數據。
  • 客戶端連接的是通過緩存而不是生成相應的原始服務器。
    在這裏插入圖片描述

旁路緩存(Cache-Aside)

  • 對象緩存是一種旁路緩存,旁路緩存通常是一個獨立的鍵值對(Key-Value)存儲。
  • 應用代碼通常會詢問對象緩存需要的對象是否存在,如果存在,它會獲取並使用緩存的對象,如果不存在或已過期,應用會連接主數據源來組裝對象,並將其保存回對象緩存中以便將來使用。
    在這裏插入圖片描述

瀏覽器對象緩存

// 在 WebStorage 中緩存對象的 JavaScript 代碼
var preferences = { /* data object to be stored    */ };
localStorage.setItem('preferences', JSON.stringify(preferences));

// 訪問緩存對象的 JavaScript 代碼
var cachedData = localStorage.getItem('preferences');
var preferences = JSON.parse(cachedData);

本地對象緩存

  • 對象直接緩存在應用程序內存中。
  • 對象存儲在共享內存,同一臺機器的多個進程可以訪問它們。
  • 緩存服務器作爲獨立應用和應用程序部署在同一個服務器上。

本地對象緩存構建分佈式集羣

在這裏插入圖片描述
小規模可以滿足需求,比如Session數據緩存。
大規模不合適:

  1. 因爲是數據都是同步的,那麼數據存儲的數量比較少。
  2. 數據同步,暫用比較大的網絡帶寬。

遠程分佈式對象緩存

在這裏插入圖片描述

Memcached 分佈式對象緩存

// PHP 客戶端訪問 Memcached 集羣
$m = new Memcached();
// 添加服務器集羣
$cache->addServers(array(
	array('cache1.example.com', 11211);
	array('cache2.example.com', 11211);
	array('cache3.example.com', 11211);
));
// 寫緩存,失效時間 5 分鐘
$m->set('userCount', 123, 600);

在這裏插入圖片描述

Memcached 分佈式緩存訪問模型

在這裏插入圖片描述
集羣是分佈式的子集。
集羣,提供相同功能服務器構建了集羣,共同對外提供服務。

怎麼知道緩存放到集羣中哪臺服務器呢?

  • 集羣路由選擇,由路由算法決定。
  • 集羣的伸縮性(添加,剔除服務器)

路由算法:

  1. 服務器地址記錄到數組列表裏面。
  2. 對Key轉換爲Hash值,Hash值對服務器總數取模,得到的餘數,就是存儲到數組中指定的服務器。

集羣的伸縮性面臨的問題:
比如以前只有3臺緩存服務器,現在加了一臺,變成4臺緩存服務器。以前模3,現在模4,那麼以前的絕大部分緩存都找不到了。就會導致服務直接訪問數據庫,就會沒法及時反應,當前服務會阻塞,上層的服務也會阻塞。這會導致緩存擊穿,增加數據庫訪問壓力,導致整個應用服務癱瘓,也是常說的集羣雪崩。

解決方案:一致性 Hash

分佈式對象緩存的一致性 Hash 算法

在這裏插入圖片描述
模哈希的最大的值,2^32次方,服務器放到環的節點上,順時針找到餘數距離最近的服務器。比如100臺服務器,增加1臺大道101臺,那麼只有1%的數據訪問不到。

有個問題:路由選擇導致有的服務器命中很高,比如一臺服務器的緩存佔了70%,其它99臺機器才緩存了30%。
解決:基於虛擬節點的一致性 Hash 算法

基於虛擬節點的一致性 Hash 算法

在這裏插入圖片描述
一個實體服務器V0, 那麼可以虛擬出5臺虛擬節點,比如V0.0, V0.1, V0.2, V0.3, V0.4, V0.5,比較均勻地分佈在環上,別的實體服務器也做相同的操作,最終就能平均緩存命中概率。

虛擬節點一致性 Hash 算法,能到到存儲負載的均衡性。

各種介質數據訪問延遲

在這裏插入圖片描述
緩存比數據庫訪問速度快100倍。請求1k數據,分佈式緩存約需要500微妙,數據庫大概需要50毫秒。

技術棧各個層次的緩存

在這裏插入圖片描述

緩存爲什麼能顯著提升性能

  • 緩存數據通常來自內存,比磁盤上的數據有更快的訪問速度。
  • 緩存存儲數據的最終結果形態,不需要中間計算,減少 CPU 資源的消耗。
  • 緩存降低數據庫、磁盤、網絡的負載壓力,使這些 I/O 設備獲得更好的響應特性。

緩存是系統性能優化的大殺器

  • 技術簡單
  • 性能提升顯著
  • 應用場景多

合理使用緩存

使用緩存對提高系統性能有很多好處,但是不合理的使用緩存可能非但不能提高系統的性能,還會成爲系統的累贅,甚至風險。實踐中,緩存濫用的情節屢見不鮮 – 過分依賴緩存、不合適的數據訪問特性等。

頻繁修改的數據:這種數據如果緩存起來,由於頻繁修改,應用還來不及讀取就已經失效或更新,徒增系統負擔。一般說來,數據的讀寫比在 2:1 以上,緩存纔有意義。

LRU(Least Recently Used)最近最少使用到的 算法

沒有熱點的訪問:緩存使用內存作爲存儲,內存的資源寶貴而有限,不能將所有數據都緩存起來,如果應用系統訪問數據沒有熱點,不遵循二八定律,即大部分數據訪問不是集中在小部分數據上,那麼緩存就沒有意義,因爲大部分數據還沒有被再次訪問就已經被擠出緩存了。
在這裏插入圖片描述
糾錯:這裏的FIFO隊列應該改爲FIFO鏈表。

數據不一致與髒讀

一般會對緩存的數據設置失效時間,一旦超過失效時間,就要從數據庫重新加載。因此應用要容忍一定時間的數據不一致,如賣家已經編輯了商品屬性,但是需要過一段時間才能被買家看到。在互聯網應用中,這種延遲通常是可以接受的,但是具體應用仍需慎重對待。還要一種策略是數據更新時立即更新緩存,不過也會帶來更多系統開銷和事務一致性的問題。因此數據更新時通知緩存失效,刪除該緩存數據,是一種更加穩妥的做法。

“計算機科學中只有三件事最困難:緩存失效,命名事物,技術錯誤。” – Phil Karlton

緩存雪崩

緩存是爲了提高數據讀取性能的,緩存數據丟失或者緩存不可用不會影響到應用程序的 – 它可以從數據庫直接獲取數據。但是隨着業務的發展,緩存承擔大部分的數據訪問壓力,數據庫已經習慣了有緩存的日子,所以當緩存服務崩潰的時候,數據庫會因爲完全不能承受如此大的壓力而宕機,進而導致整個網站不可用。這種情況,被稱作緩存雪崩,發生這種故障,甚至不能簡單的重啓緩存服務和數據庫服務器來回復網站訪問。

緩存預熱

緩存中存放的是熱點數據,熱點數據又是緩存系統利用 LRU (最近最久未用)算法對不斷訪問的數據篩選淘汰出來的,這個過程需要花費較長的時間,在這段時間,系統的性能和數據庫負載都不太好,那麼最好的緩存系統啓動的時候就把熱點數據加載好,這個緩存預加載叫做緩存預熱(Warm up)。對於一些元數據,比如城市地名列表、類目信息,可以啓動時加載數據庫中全部數據到緩存進行預熱。

緩存穿透

如果不恰當的業務、或者惡意攻擊持續高併發的請求某個不存在的數據,因爲緩存沒有保存該數據,所有的請求都會落到數據庫上,會對數據庫造成很大的壓力,甚至崩潰。一個簡單的對策是將不存在的數據也緩存起來(其Value值爲null),並設定一個較短的失效時間。

Redis

Redis vs Memcached

  • Redis 支持複雜的數據結構
  • Redis 支持多路複用異步 I/O 高性能
  • Redis 支持主從複製高可用
  • Redis 原生集羣與 Share Nothing 集羣模式

在這裏插入圖片描述

Redis 集羣

  • Redis 集羣預分好 16384 個桶,當需要在 Redis 集羣中放置一個 Key-Value 時,根據 CRC16(key) mode 16384 的值,決定將一個key放到哪個桶中。
  • Redis-Cluster 把所有的物理節點映射到 [0-16383] Slot上(不一定是平均分配),Cluster負責維護 Slot 與服務器的映射關係。
  • 客戶端與 Redis 節點直連,客戶端不需要連接集羣所有節點,連接集羣中任何一個可用節點即可。
  • 所有的 Redis 節點彼此互聯。
    在這裏插入圖片描述
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章