Redis 集羣的合縱與連橫(分佈式技術)

以前一篇寫了關於 Redis 的性能,這篇就寫寫我認爲比性能更重要的擴展性方面的主題。前端

若是再給我一次回到好幾年前的機會,對於使用 Redis 我一開始就要好好考慮未來的擴展問題。就像咱們作數據庫分庫分表,一旦決策了分庫分表,一般一次就會分到位,好比搞上 8 或 16 個庫,每一個庫再分 256 或 1024 個表。無論未來業務再怎麼發展,基本這個量級的分片都足夠應對,並且底層庫能夠作成邏輯的,扛不住時再換成物理的,對應用方徹底透明,沒有數據遷移的煩惱。算法

而 Redis 其實也提供了相似的邏輯庫概念,每一個 Redis 實例都有 0 到 15 號獨立的邏輯庫空間。當咱們早期機器資源緊張而業務量又不大時,能夠好好根據業務把不一樣的數據放在的單一實例的不一樣編號邏輯庫上。這是一種垂直切分方式,也能夠用水平方式,把 0 到 15 號邏輯庫當成 16 個分片來用,只是這種用法可能對 Client 庫有些要求。數據庫

總之好幾年前咱們都沒有這樣,當時物理機資源緊張,爲了考慮不遠未來的業務擴張,因此在有限的資源下決定儘量的分片。但也沒分太多,大約 10 片吧,多了運維成本也高。感受按 Redis 的性能這一組分片最大承載幾十萬每秒的 OPS 估計能支撐很長時間的發展了。那 10 片怎麼部署呢?因爲每一個 Reids 實例只能利用一個核,當時的服務器大概是 16 核,全放一臺機也能夠。當時咱們正好有 10 臺物理機,因此很天然的每臺放了一個實例,但 Redis 只能用一個核,太浪費了。因此每臺物理機上除了部署 Reids 還要部署應用服務,後來領悟到這又是一個錯誤的部署方式(背景音樂:多麼痛的領悟)。後端

一臺 PC Server 的硬件可靠性大約是 99.9%,Redis 做爲一個應用全局共享的關鍵服務分紅 10 片放在十臺 PC Server 上。實際上致使總體系統可靠性還下降了一個量級,變成了兩個 9。由於任何一臺 PC Server 掛了均可能致使全局系統故障。然而當初沒有多餘的機器資源,爲了提升可靠性,必須對 Redis 作主備,惟一的辦法就是交叉主備,因此部署結構大概相似下面這樣。服務器

後來,隨着業務發展流量變得愈來愈大,Redis 內存佔用愈來愈多,並且開始出現一些詭異的故障現象。好比出現瞬時 Redis 大量鏈接和處理超時,應用業務線程被阻塞,致使服務拒絕,過一段時間可能又自動恢復了。這種瞬時故障很是難抓現場,一天來上幾發就會給人業務不穩定的感覺,而通常基礎機器指標的監控週期在分鐘級。瞬時故障可能發生在監控的採集間隙,因此只好上腳本在秒級監控日誌,發現瞬時出現大量 Redis 超時錯誤,就收集當時應用的 JVM 堆棧、內存和機器 CPU Load 等各項指標。終於發現瞬時故障時刻 Redis 機器 CPU Load 出現瞬間飆升幾百的現象,應用和 Redis 混合部署時應用可能瞬間搶佔了所有 CPU 致使 Redis 沒有 CPU 資源可用。而應用處理業務的邏輯又可能須要訪問 Redis,而 Redis 又沒有 CPU 資源可用致使超時,這不就像一個死鎖麼。搞清楚了緣由其實解決方法也簡單,就是分離應用和 Redis 的部署,各自資源隔離,自此咱們的 Redis 集羣發展開始走上一條合縱與連橫的道路。架構

合縱

分離應用和 Redis 的部署後,關於物理機資源的有一個尷尬點就是 Redis 單線程機制帶來的。當時一臺 PC Server 16 核,內存 16 G,你想多利用核就要多部署實例,但每一個實例分到的內存又很少。最終咱們一臺物理機只部署 2 個實例,由於業務發展對內存的需求強過對 CPU 的利用,因此調整後的部署模型變成下面這樣。框架

這樣每一個 Redis 實例能分到的內存是小於 8G 的(還要給系統留一點不是)。隨着業務發展,一開始是 2G,很快 4G 而後 6G 就到了單機內存瓶頸,下一步只能分一個實例出去,每一個實例獨享單機內存。縱向擴容在操做性上是最簡單的,在另一臺機器上先掛一個從分片,同步複製完成後,通知 Client 端切換鏈接而分片 Hash 規則仍是不變。這個過程會有短暫的(下圖 2-5 步這個過程程序執行的時間窗口)寫丟失,在業務上是可接受的。運維

獨享了更大內存,咱們就能夠繼續縱向擴內存,但擴到了 12G 後就基本到頂了,即使還有更大內存的物理機也不宜再擴大單分片的內存了。主要緣由是由於 Redis 的主從複製致使的服務中斷,當初 Redis 版本是 2.4,直到 2.8 纔有增量的主從複製。即便 2.8 主從複製依然可能在斷鏈長時間後致使全量複製,雖然官方文檔號稱主從複製不中斷服務,但實際每次全量複製 dump 內存時是阻斷了主線程執行。這個阻斷時間在 12G 內存時大概有一分多鐘, 繼續縱向擴大內存會致使更長時間的阻斷,在業務上不可接受,合縱之路也走到頭了。分佈式

連橫

爲了對業務作到無縫透明的擴容,只能走橫向發展的道路。而 Redis 官方的 Cluster 方案一直跳,遲遲出不來,你們的業務都在快速發展,等不及啊。因此在橫向擴展上演變出了兩種方案,一種是代理模式,利用引入中間 Proxy 來嚮應用層屏蔽後端的集羣分佈。業界最先是 Twitter 開源的 Twemproxy 採用了這種模式,後來豌豆莢開源的 Codis 進一步在運維可操做性上完善了這種模式。主要是在擴容方面儘量的作到業務無感知,思路就是前端引入 Proxy 隔離應用層,後端改造 Redis 引入 Slot(有些也叫 Buket)來分組 key。應用層訪問時基於算法將 key 先映射到 Slot 再映射到具體分片實例,大概以下面這樣。性能

F(key) -> Slot -> Instance

Redis 中的 key 按 Slot 來組織,平滑擴容時好比加分片後,按 Slot 爲單位遷移,這一般須要改造 Redis 源碼來支持。這個模式的架構示意圖以下所示。

引入 Proxy 是犧牲了少許性能來換取了對應用的透明和更好的擴展性。另外一種方案是基於 Smart Client 免代理,但對應用有必定的侵入性,本質上就是把 Proxy 的功能放到了 Client。

至於採用哪一種方案就是仁者見仁、智者見智了,須要根據實際狀況去考慮。不過我的認爲基於代理的方案更靈活些,並且可在 Proxy 層能作的事情比 Client 要多,但對 Proxy 的實現要求也更高。無論上面哪一種方案都採用了中心化的控制方式,中心化對簡化運維操做是有好處的,並且能方便作到集羣全局的管理。

Redis Cluster 終於遲遲推出後,採用了與中心化不一樣的思路,並且設計目標更追求性能,因此是依賴 Smart Client 的方式。而 Redis Server 的實現仍是使用了 Slot 方式默認最大 16 * 1024 = 16384 個 Slot,因此理論集羣最大就是這麼多個實例,實際不大可能須要這麼大。採用 Gossip 消息來同步集羣配置,基於投機制來進行主從 Failover 發現和自動切換。從目前已經推出的版本和功能來看,做者是在往一個純 Smart Cluster 方向發展,但顯然目前的版本還不成熟。好比自動發現、集羣智能再平衡等功能都沒有,還依賴人工操做。並且非中心化的集羣相比中心化集羣的可預測性和操做性都要差很多,其在真實的應用案例,除了在網易有道有人分享了一個非關鍵類場景,還沒見過比較有分量的成熟案例。因此 Redis Cluster 的道路恐怕還在路漫漫其修遠兮,吾將上下而求索的階段。

總結

前面回顧了 Redis 集羣發展的歷程,從合縱到連橫實際是一個從業務垂直切分到平臺服務化的過程。至於到底應該採用什麼樣的集羣模式,可能須要好好結合自身的業務發展階段、團隊能力和企業環境去分析取捨了。沒有最好的,只有合適的。

分佈式框架介紹 - kafkaee - kafkaee的博客

   項目模塊依賴

分佈式框架介紹 - kafkaee - kafkaee的博客

分佈式框架介紹 - kafkaee - kafkaee的博客

分佈式框架介紹 - kafkaee - kafkaee的博客

相關文章
相關標籤/搜索