天天看點

用Redis建構緩存叢集的最佳實踐有哪些?Redis Cluster 如何解決資料量大、高可用和高并發問題?那 Redis Cluster 是怎麼解決高可用問題的?為什麼 Redis Cluster 不适合超大規模叢集?小結

Redis Cluster 如何解決資料量大、高可用和高并發問題?

Redis 從 3.0 版本開始,提供了官方的叢集支援,也就是 Redis Cluser。Redis Cluster 相比于單個節點的 Redis,能儲存更多的資料,支援更多的并發,并且可以做到高可用,在單個節點故障的情況下,繼續提供服務。

為了能夠儲存更多的資料,和 MySQL 分庫分表的方式類似,Redis Cluster 也是通過分片的方式,把資料分布到叢集的多個節點上。

Redis Cluster 是如何來分片的呢?它引入了一個“槽(Slot)”的概念,這個槽就是哈希表中的哈希槽,槽是 Redis 分片的基本機關,每個槽裡面包含一些 Key。每個叢集的槽數是固定的 16384(16 * 1024)個,每個 Key 落在哪個槽中也是固定的,計算方法是:

HASH_SLOT = CRC16(key) mod 16384
           

複制

這個算法很簡單,先計算 Key 的 CRC 值,然後把這個 CRC 之後的 Key 值直接除以 16384,餘數就是 Key 所在的槽。這個算法就是我們上節課講過的哈希分片算法。

這些槽又是如何存放到具體的 Redis 節點上的呢?這個映射關系儲存在叢集的每個 Redis 節點上,叢集初始化的時候,Redis 會自動平均配置設定這 16384 個槽,也可以通過指令來調整。這個分槽的方法,也是我們上節課講到過的分片算法:查表法

用戶端可以連接配接叢集的任意一個節點來通路叢集的資料,當用戶端請求一個 Key 的時候,被請求的那個 Redis 執行個體先通過上面的公式,計算出這個 Key 在哪個槽中,然後再查詢槽和節點的映射關系,找到資料所在的真正節點,如果這個節點正好是自己,那就直接執行指令傳回結果。如果資料不在目前這個節點上,那就給用戶端傳回一個重定向的指令,告訴用戶端,應該去連哪個節點上請求這個 Key 的資料。然後用戶端會再連接配接正确的節點來通路。

解決分片問題之後,Redis Cluster 就可以通過水準擴容來增加叢集的存儲容量,但是,每次往叢集增加節點的時候,需要從叢集的那些老節點中,搬運一些槽到新節點,你可以手動指定哪些槽遷移到新節點上

分片可以解決 Redis 儲存海量資料的問題,并且客觀上提升了 Redis 的并發能力和查詢性能。但是并不能解決高可用的問題,每個節點都儲存了整個叢集資料的一個子集,任何一個節點當機,都會導緻這個當機節點上的那部分資料無法通路

那 Redis Cluster 是怎麼解決高可用問題的?

增加從節點,做主從複制。Redis Cluster 支援給每個分片增加一個或多個從節點,每個從節點在連接配接到主節點上之後,會先給主節點發送一個 SYNC 指令,請求一次全量複制,也就是把主節點上全部的資料都複制到從節點上。全量複制完成之後,進入同步階段,主節點會把剛剛全量複制期間收到的指令,以及後續收到的指令持續地轉發給從節點。

最後我們看一下,Redis Cluster 是如何應對高并發的

一般來說,Redis Cluster 進行了分片之後,每個分片都會承接一部分并發的請求,加上 Redis 本身單節點的性能就非常高,是以大部分情況下不需要再像 MySQL 那樣做讀寫分離來解決高并發的問題。預設情況下,叢集的讀寫請求都是由主節點負責的,從節點隻是起一個熱備的作用。當然了,Redis Cluster 也支援讀寫分離,在從節點上讀取資料。

以上就是 Redis Cluster 的基本原理,你可以對照下圖來加深了解

用Redis建構緩存叢集的最佳實踐有哪些?Redis Cluster 如何解決資料量大、高可用和高并發問題?那 Redis Cluster 是怎麼解決高可用問題的?為什麼 Redis Cluster 不适合超大規模叢集?小結

Redis Cluster 整體的架構完全就是照抄 MySQL 建構叢集的那一套東西(當然,這些設計和方法也不是 MySQL 發明的),抄作業抄的就差把名字一起也抄上了

為什麼 Redis Cluster 不适合超大規模叢集?

Redis Cluster 的優點是易于使用。分片、主從複制、彈性擴容這些功能都可以做到自動化,通過簡單的部署就可以獲得一個大容量、高可靠、高可用的 Redis 叢集,并且對于應用來說,近乎于是透明的

是以,Redis Cluster 是非常适合建構中小規模 Redis 叢集,這裡的中小規模指的是,大概幾個到幾十個節點這樣規模的 Redis 叢集

但是 Redis Cluster 不太适合建構超大規模叢集,主要原因是,它采用了去中心化的設計。剛剛我們講了,Redis 的每個節點上,都儲存了所有槽和節點的映射關系表,用戶端可以通路任意一個節點,再通過重定向指令,找到資料所在的那個節點。那你有沒有想過一個問題,這個映射關系表,它是如何更新的呢?比如說,叢集加入了新節點,或者某個主節點當機了,新的主節點被選舉出來,這些情況下,都需要更新叢集每一個節點上的映射關系表。

如何用 Redis 建構超大規模叢集?

  • Redis Cluster 不太适合用于大規模叢集,是以很多大廠,都選擇自己去搭建 Redis 叢集。這裡面,每一家的解決方案都有自己的特色,但其實總體的架構都是大同小異的。
  • 一種是基于代理的方式,在用戶端和 Redis 節點之間,還需要增加一層代理服務。這個代理服務有三個作用。
  • 第一個作用是,負責在用戶端和 Redis 節點之間轉發請求和響應。用戶端隻和代理服務打交道,代理收到用戶端的請求之後,再轉發到對應的 Redis 節點上,節點傳回的響應再經由代理轉發傳回給用戶端。
  • 第二個作用是,負責監控叢集中所有 Redis 節點狀态,如果發現有問題節點,及時進行主從切換。第三個作用就是維護叢集的中繼資料,這個中繼資料主要就是叢集所有節點的主從資訊,以及槽和節點關系映射表
用Redis建構緩存叢集的最佳實踐有哪些?Redis Cluster 如何解決資料量大、高可用和高并發問題?那 Redis Cluster 是怎麼解決高可用問題的?為什麼 Redis Cluster 不适合超大規模叢集?小結

用 HAProxy+Keepalived 來代理 MySQL 請求的架構是類似的,隻是多了一個自動路由分片的功能而已

當然,用戶端不用每次都去查詢中繼資料,因為這個中繼資料是不怎麼變化的,用戶端可以自己緩存中繼資料,這樣通路性能基本上和單機版的 Redis 是一樣的。如果某個分片的主節點當機了,新的主節點被選舉出來之後,更新中繼資料裡面的資訊。對叢集的擴容操作也比較簡單,除了遷移資料的工作必須要做以外,更新一下中繼資料就可以了

用Redis建構緩存叢集的最佳實踐有哪些?Redis Cluster 如何解決資料量大、高可用和高并發問題?那 Redis Cluster 是怎麼解決高可用問題的?為什麼 Redis Cluster 不适合超大規模叢集?小結

雖然說,這個中繼資料服務仍然是一個單點,但是它的資料量不大,通路量也不大,相對就比較容易實作。我們可以用 ZooKeeper、etcd 甚至 MySQL 都能滿足要求。這個方案應該是最适合超大規模 Redis 叢集的方案了,在性能、彈性、高可用幾方面表現都非常好,缺點是整個架構比較複雜,用戶端不能通用,需要開發定制化的 Redis 用戶端,隻有規模足夠大的企業才負擔得起

小結

從小到大三種建構 Redis 叢集的方式

  • 小規模的叢集建議使用官方的 Redis Cluster,在節點數量不多的情況下,各方面表現都不錯。
  • 再大一些規模的叢集,可以考慮使用 twemproxy 或者 Codis 這類的基于代理的叢集架構,雖然是開源方案,但是已經被很多公司在生産環境中驗證過。
  • 相比于代理方案,使用定制用戶端的方案性能更好,很多大廠采用的都是類似的架構
用Redis建構緩存叢集的最佳實踐有哪些?Redis Cluster 如何解決資料量大、高可用和高并發問題?那 Redis Cluster 是怎麼解決高可用問題的?為什麼 Redis Cluster 不适合超大規模叢集?小結

繼續閱讀