架構師視角 | 分佈式緩存如何選擇 ?

現在,緩存系統的應用很是普遍,可以用來提升併發數、數據吞吐量,提升快速響應能力。那麼當數據量達到必定程度,單機環境可能就顯得有些力不從心了,就須要一個分佈式緩存系統。java

1. 緩存系統的選擇

1.1 緩存分類node

如上圖所示,首先緩存大體能夠分爲四大類。程序員

  • CDN 緩存:CDN 即內容分發網絡,CDN 邊緣節點將數據緩存起來。
  • 反向代理緩存:如 Nginx 的緩存。
  • 本地緩存:表明的有 EhCache 和 Guava Cache。
  • 分佈式緩存:各緩存系統。

1.2 分佈式緩存redis

本文主要探討各分佈式緩存系統,如圖 1-1 所示,列出了五種:算法

其中 EvCache 和 Aerospike 使用場景不是那麼通用和普遍。spring

  • EvCache:是 Netflix 的基於 Memcached & Spymemcached 的緩存方案。
  • Aerospike:是可基於 SSD 的 KV NoSQL 數據庫。

除此以外,還有三種常見緩存系統。數據庫

  • Tair:阿里開源,跨機房、性能隨結點添加線性上升、適用大數據量。Tair 還有三種引擎。
    • LDB: 基於 google levelDB,支持 KV和類 HashMap 結構,性能稍低,持久化可靠性最高。
    • MDB: 基於 Memcache,支持 KV 和類 HashMap,性能最優,不支持持久化存儲。
    • RDB: 基於 Redis。
  • Memcache: 不支持數據同步、分佈式支持較差。
  • Redis: 社區活躍、使用最多。

綜上所述,在通常狀況下,考慮到適用性和穩定性,Redis 是搭建緩存系統的最優選擇。如下將基於 Redis 介紹。設計模式

2. Redis 集羣緩存方案

如頂部圖 1-1 所示,列出了 Redis 的集羣高可用的方案,基本能夠分爲三種。緩存

2.1 主從機制服務器

常見的集羣架構,搭建簡單,主要實現讀寫分離和備份,能夠由 Master 負責讀寫,Slave 負責備份。但存在故障恢復複雜、水平拓展難、寫能力受限等問題。結構圖以下:

2.2 哨兵機制

Redis Sentinel 是社區版本推出的原生高可用解決方案。由一或多個哨兵實例監視任意個主從服務器,且在 Master 宕機時,自動將宕機服務器屬下的 Slave 服務器升級爲 主服務器,從而保證系統的可用性。較主從實現的監控、選主。但問題主要是要保證 Master 的 HA 切換。結構圖以下:

2.3 "分佈式"

到這裏以上兩種機制其實只能算做「集羣」,並不是嚴格意義上的「分佈式」。接着來看看分佈式方案。

集羣強調高可用,分佈式在集羣的基礎上又強調協做。

3. Redis分佈式緩存方案

任何分佈式存儲系統,首先面臨的就是 sharding(分片)問題,如頂部圖 1-1 所示該問題有爲三種解決方法。

3.1 客戶端分片

顧名思義,將數據分片的路由功能交給客戶端,但這是一種靜態分片,維護性差。基本是不予考慮的。

3.2 代理分片

經過代理分發到具體的 redis 實例。有兩個經常使用解決方案。

  • Twemproxy:Twitter 開源,輕量級,再也不維護,沒法平滑地擴容/縮容,運維也不是很友好,性能通常。
  • Codis:豌豆莢開源,支持水平拓展,運維平臺完善,性能較 Twemproxy 快。Codis 在國內使用的較多,同時代理分片的思路也有不少公司在此基礎開發了本身的二次方案。不過 Codis 也再也不維護。

其實,這兩種代理分片的方案,都是在 Redis 官方未推出良好的分佈式方案時的產生的,在官方更新提供更優策略後都再也不維護。

3.3 服務器端分片

這就要談到 Redis 官方方案 Redis-cluster 。

在 Redis 3.0 以前是沒有較好的分佈式方案的,這也是第三方方案出現的緣由。3.0 開始,官方推出了去中心化的分佈式方案。集羣中包含 16384 個散列槽,每一個節點負責其中一部分。

先看下拓撲圖:

每一個節點打開兩個 TCP 鏈接,一個負責給客戶端提供服務,一個負責節點間通訊。

此刻要說說 CAP 了 :Consistency(一致性)、Availability(可用性)、Partition tolerance(分區容錯性) 。對分佈式系統而言,CAP 必須犧牲一者。Redis Cluster 的設計目標主要是高性能、高可用和高擴展,只好拋棄一部分數據一致性。

  • 數據一致性:因爲Redis Cluster 使用異步複製, 在某些狀況下如 Master 宕機但未同步至 Slave,可能會致使丟失寫入。在絕對須要支持同步寫入時,可經過 WAIT 命令實現,可以使得丟失寫入的可能性大大下降。
  • 可用性:當集羣中一部分節點故障後,集羣總體能響應客戶端讀寫請求。
    • 節點間定時互 ping ,當超過一半 Master 斷定某節點失敗,則標記爲 FAIL,且會向集羣廣播節點下線的消息。以下線節點是帶有槽的主節點,則要從它的從節點選出一個替換。
  • 高性能和拓展:操做某個 key 時,不會先找到節點再處理,而是直接直接重定向到該節點,同時相較代理分片也少了 proxy 的鏈接損耗。
    • 可是在進行 multiple key 操做時須要 keys 位於同一個 slot 上,須要使用 hash tags,使用 {} 強制將某些 key 映射到每一個 slot,以便進行 multiple 。
    • 在拓展方面,Redis Cluster 最大支持線性拓展 1000 個節點,將新節點加入集羣后能夠經過命令指定和平均的從已有節點分配 slot。

4. 緩存常見問題

以上介紹了簡單介紹了常見緩存系統,並具體列出了基於 Redis 的集羣方案。下面談一談緩存系統常見的問題。

以下圖所示,列出七個常見問題。

4.1. 緩存穿透

指訪問不存在的數據,從而繞過緩存,直接請求到了數據源,當請求過多,就會對 DB 形成壓力。

  • 空 key:指對於不存在的數據也將 key 存空值入緩存系統,這樣下次訪問也會獲得返回。但只適用於空數據 key 有限、key 重複請求機率高,若是量大且不重複,就會形成不少無用 key 的建立。
  • 布隆過濾器:布隆過濾器是一個很長的二進制向量和一系列隨機映射函數。可用於檢索一個元素是否在一個集合中加一層對空值的過濾器,空間和時間效率都很高。但因爲 hash 產生的碰撞可能存在誤判,以及因不存儲 key 致使的沒法刪除。適用於空數據 key 各不一樣、重複請求機率低。

4.2. 緩存擊穿

緩存擊穿實際是緩存雪崩的一個特例。指當某些熱點 key 過時時,就會有大量的請求擊穿到 DB。

  • 互斥鎖:在緩存失效的時候,不當即 load db,能夠先用如 SETNX 等命令去 set 一個 mutex key,當操做返回成功時,說明拿到鎖,此刻該線程進行 load db 的操做並更新緩存;不然未拿到鎖就(可休眠一段)重試 get 緩存的方法。但要注意死鎖風險。
  • 不過時
    • 這裏的不過時有兩個概念,一個指未設過時時間,那是真的不過時,那沒事了。
    • 另外一個是指經過業務邏輯,將 key 的過時時間進行存儲,請求是判斷是否小於值,是則後臺異步更新。

4.3. 緩存雪崩

同一時刻大量緩存失效(故障), 請求到了 DB。

  • 隨機時間:在設置過時時間時,能夠在基礎時間上 + 一個隨機的時間,等於實現了分批過時。
  • 後臺更新:將更新失效的工做交給後臺定時線程。
  • 限流 + 本地緩存:如 ehcache 本地緩存 + Hystrix 限流。
  • 雙緩存:相似於設置主從緩存,從 key 不過時。

4.4. 緩存更新與一致性

若是保證數據一致性。列出四種更新策略:

  • Cache Aside :最經常使用的。失效時回源取數據,更新;命中時,返回緩存數據;更新時先數據源更新,再更新緩存。
  • Write Back :更新數據時,只更新緩存,不更新數據源。緩存異步批量更新數據庫。
  • Read/Write Through
    • Write Through :當有數據更新時,如未命中緩存,直接更新數據庫,並返回。如命中緩存,則更新緩存,再由 Cache 本身更新數據庫。
    • Read Through :更新數據源由緩存系統操做,讀取數據時如緩存失效,則取回源數據更新緩存。

4.5. 熱點數據

對於熱點數據的處理方法。

  • 拆分複雜結構:如二級數據結構,進行拆分,這樣熱點 key 就被拆爲若干個的 key 分佈到不一樣節點。
  • 遷移熱點:對於 Redis Cluster 而言能夠將熱點 key 所在的 slot 單獨遷移到一個節點,下降其餘節點壓力。
  • 多副本:複製多份緩存副本,將請求分散到多個節點上,減輕單臺緩存服務器壓力,適合多讀少寫。

4.6. 緩存預熱

指能夠將某些的緩存數據提早加載到緩存系統,提早避免在如熱點數據大量請求到庫。

4.7. 緩存降級

指當訪問量劇增、服務出現問題或非核心服務影響到核心流程的性能時,仍需保證主服務可用。可根據一些關鍵數據自動降級,也可配置開關人工降級。

5. Redis Cluster 使用

對於 Redis Cluster 環境的搭建和基礎使用很是簡單。

不管基於何種方式,只要搭建好 n 臺 redis 服務並保證各服務間能夠互相通信後,任意進入一個 redis 服務鍵入:

redis-cli --cluster create IP1:port1 IP2:port2 IP3:port3 IP4:port4 IP5:port5 IP6:port6 ... --cluster-replicas 1

便可。以後可使用 cluster node 和 cluster info 命令查看集羣、節點信息。

而對於廣大 JAVA 開發,Spring Data Redis 從 1.7 起即支持 Redis Cluster,只需配置 Master 節點地址(和密碼)。

spring.redis.cluster.nodes=ip1:port1,ip2:port2,ip3:port3

加入依賴

compile("org.springframework.boot:spring-boot-starter-data-redis")

便可經過 RedisTemplate 使用。

6. 總結

本文從緩存系統的選擇出發,基於 Redis 介紹了幾種集羣方案並重點說明了 Redis Cluster 方案。以後列出緩存系統常見問題及常看法決方案,最後對使用作了簡單說明。

固然,如何去落地,如何解決這些問題還須要根據實際場景具體分析和處理。

推薦閱讀:

======

MySQL從入門到進階教程,主講老師:馬士兵、連鵬舉

字節跳動總結的設計模式 PDF 火了,完整版開放分享

刷Github時發現了一本阿里大神的算法筆記!標星70.5K

若是能聽懂這個網約車實戰,哪怕接私活你均可以月入40K

爲何阿里巴巴的程序員成長速度這麼快,看完他們的內部資料我懂了

程序員達到50W年薪所須要具有的知識體系。

關於【暴力遞歸算法】你所不知道的思路

看完三件事❤️

若是你以爲這篇內容對你還蠻有幫助,我想邀請你幫我三個小忙:

點贊,轉發,有大家的 『點贊和評論』,纔是我創造的動力。

關注公衆號 『 Java鬥帝 』,不按期分享原創知識。

同時能夠期待後續文章ing🚀

相關文章
相關標籤/搜索