[原創]分散式系統之緩存的微觀應用經驗談(三)【數據分片和集群篇】

来源:https://www.cnblogs.com/bsfz/archive/2018/10/22/9833600.html
-Advertisement-
Play Games

第三篇這裡嘗試談談緩存的數據分片(Sharding)以及集群(Cluster)相關方案(具體應用依然以Redis 舉例)另見:分散式系統之緩存的微觀應用經驗談(二) 【主從和主備高可用篇】( https://www.cnblogs.com/bsfz/) 一、先分析緩存數據的分片(Sharding) ... ...


分散式系統之緩存的微觀應用經驗談(三)【數據分片和集群篇】

 


前言

  近幾個月一直在忙些瑣事,幾乎年後都沒怎麼閑過。忙忙碌碌中就進入了2018年的秋天了,不得不感嘆時間總是如白駒過隙,也不知道收穫了什麼和失去了什麼。最近稍微休息,買了兩本與技術無關的書,其一是 Yann Martel 寫的《The High Mountains of Portugal》(葡萄牙的高山),發現閱讀此書是需要一些耐心的,對人生暗喻很深,也有足夠的留白,有興趣的朋友可以細品下。好了,下麵回歸正題,嘗試寫寫工作中緩存技術相關的一些實戰經驗和思考。


正文

  在分散式Web程式設計中,解決高併發以及內部解耦的關鍵技術離不開緩存和隊列,而緩存角色類似電腦硬體中CPU的各級緩存。如今的業務規模稍大的互聯網項目,即使在最初beta版的開發上,都會進行預留設計。但是在諸多應用場景里,也帶來了某些高成本的技術問題,需要細緻權衡。本系列主要圍繞分散式系統中服務端緩存相關技術,也會結合朋友間的探討提及自己的思考細節。文中若有不妥之處,懇請指正。

  為了方便獨立成文,原諒在內容排版上的一點點個人強迫症。

  第三篇這裡嘗試談談緩存的數據分片(Sharding)以及集群(Cluster)相關方案(具體應用依然以Redis 舉例)

  另見:分散式系統之緩存的微觀應用經驗談(二) 【主從和主備高可用篇】(https://www.cnblogs.com/bsfz/


一、先分析緩存數據的分片(Sharding)

  (註:由於目前個人工作中大多數情況應用的是Redis 3.x,以下若有特性關聯,均是以此作為參照說明。)

 

  緩存在很多時候同 RDBMS類似,解決數據的分散式存儲的基礎理念就是把整個數據集按照一定的規則(切分演算法)映射到多個節點(node)中,每個 node負責處理整體數據的一個子集。給緩存作 Sharding 設計,圍繞基礎數據的存儲、通信、數據複製和整合查詢等,很多時候比較類似 RDBMS中的水平分區(Horizontal Partitioning),事實上很多點在底層原理上是保持一致的。在緩存的分區策略中,最常見的是基於哈希的各種演算法。

 

  1.1 基於 Round Robin

 

    實現思路是以緩存條目的標識進行哈希取餘,如對緩存中的 Key 進行 Hash計算,然後將結果 R 與 node 個數 N 進行取餘,即 R%N 用來指定數據歸屬到的 node索引。

    個人認為在數據量比較固定並有一定規律的場景下,則可以考慮基於這種方式的設計。在落地實踐前需要註意,這種方案看起來簡潔高效,但卻無法良好的解決 node的彈性伸縮問題,比如數量 N發生變化時均需要重新覆蓋計算,存儲的數據幾乎是重新遷移甚至重置,對數據的支撐本身會有些勉強和局限。另外,早期使用手動進行預分區,後來增加了一些相應的路由策略可進行翻倍擴容等,都可考慮作為實踐場景中的某些細小優化和輔助。

 

  1.2 基於 Consistent Hashing

 

    實現思路是將 node 進行串聯組合形成一個 Hash環,每個 node 均被分配一個 token作為 sign,sign取值範圍對應 Hash結果區間,即通過Hash計算時,每個 node都會在環上擁有一個獨一無二的位置,此時將緩存中的Key做基本Hash計算,根據計算結果放置在就近的 node 上,且 node 的 sign 大於等於該計算結果。

    這種策略的優勢體現在,當數據更迭較大,動態調整node(增加/刪除)隻影響整個 Hash環中個別相鄰的 node,而對其他 node則無需作任何操作。也就是說,當數據發生大量變動時,可以有效將影響控制在局部區間內,避免了不必要的過多數據遷移,這在緩存的條目非常多、部署的 node也較多的時候,可以有效形成一個真正意義上的分散式均衡。但在架構落地之前,這種方案除了必要的對 node 調整時間的額外控制,還需要權衡下緩存數據的體量與 node 的數量進行比較後的密集度,當這個比值過大時,數據影響也自然過大,這個就是不符合設計初衷的,不僅沒有得到應有的優化,反而增加了一定的技術成本。

 

  1.3 基於 Range Partitioning

 

    實現思路實際是一種增加類似中間件的包裝思想,首先將所有的緩存數據統一划分到多個自定義區間(Range),然後將這些 Range逐一綁定到關聯服務的各個 node中,每個 Range將在數據變化時進行相應調整以達到均衡負載。

    這其實並非是一個完全新穎的策略,但針對大數據的劃分和交互做了更多的考慮。以 Redis的 Sharding演算法類比,截止目前的集群方案(Redis Cluster,以3.x舉例)中,其策略同樣包含了 Range這一元素概念。Redis採用虛擬槽(slot)來標記,數據合計 16384個 slot。緩存數據根據 key進行 Hash歸類到各個 node綁定的 slot。當動態伸縮 node時,針對 slot做相應的分配,即間接對數據作遷移。

 

    這種上層的包裝,雖然極大地方便了集群的關註點和線性擴展,在目前落地方案里 Redis也已經儘力擴展完善,但依然還是處於半自動狀態。集群是分佈的 N個 node,如需要伸展為 N+2個node,那麼可以手動或者結合其他輔助框架給每個 node進行劃分和調整,一般均衡數約為 16384 /(N+2) slot。同樣的,Cluster 的Sharding 在實際落地之前,也需要註意到其不適合的場景,並根據實際數據體量和 QPS瓶頸來合理擴展node,同時處理事務型的應用、統計查詢等,對比單機自然是效率較低,這時候可能需要權衡規避過多的擴展,越多從來都不代表越穩定或者說性能越高。


二、談談緩存的集群實踐與相關細節

  2.1 提下集群的流程

    我在之前一篇文章里,主要圍繞主從和高可用進行了一些討論(主從和主備高可用篇:https://www.cnblogs.com/bsfz/p/9769503.html),要提出的是 Master-Slave 結構上來說同樣算是一種集群的表現形式,而在 Redis Cluster 方案里,則更側重分散式數據分片集群,性能上能規避一些冗餘數據的記憶體浪費以及木桶效應,並同時具備類似 Sentinel機制的 HA和 Failover等特性(但要註意並非完全替換)。

    這裡同樣涉及到運維、架構 、開發等相關,但個人依然側重於針對架構和開發來做一些討論。 當然,涉及架構中對網路I/O、CPU的負載,以及某些場景下的磁碟I/O的代價等問題的權衡,其實大體都是相通的,部分可參見之前文章中的具體闡述。

    簡單說在集群模式 Redis Cluster 下, node 的角色分為 Master node 和 Slave node,明面上不存在第三角色。 Master node 將被分配一定範圍的 slots 作為數據 Sharding 的承載,而 Slave node 則主要負責數據的複製(相關交互細節可參照上一篇) 併進行出現故障時半自動完成故障轉移(HA的實現)。node之間的通信依賴一個相對完善的去中心化的高容錯協議Gossip, 當擴展node、node不可達、node升級、 slots修改等時, 內部需要經過一段較短時間的反覆ping/pong消息通信, 並最終達到集群狀態同步一致。

 

    以 Redis 3.x 舉例,假定一共 10 個 node,Master / Slave = 5 / 5,執行基礎握手指令 " cluster meet [ip port] "後,就能很快在 cluster nodes里看到相應會話日誌信息,在這個基礎上,再給每個node 添加指定 Range 的 lots( addslots指令),就基本完成了 Redis Cluster的基礎構建。(當然,如果是側重運維,一般你可以手動自定義配置,也可以使用 redis-trib.rb來輔助操作,這裡不討論)。

  2.2 Redis Cluster的部分限制

    Redis node的拓撲結構設計,目前只能採用單層拓撲,即不可直接進行樹狀延伸擴展node,註意這裡是不同於 Redis Mater-Slave 基本模式的,然後記得在上一篇也提到了本人迄今為止也並未有機會在項目中使用,也是作為備用。

 

    對於原有 DB空間的劃分基本等同取消,這個有在第一篇設計細節話題中提到過,並且 Redis Cluster 模式下只能預設使用第一個DB, 即索引為0 標識的庫。

    假如存在大數據表 “Table”,例如 hash、list 等,是不可以直接採用更細粒度的操作來 Sharding 的,即使強制分散到不同 node 中,也會造成 slot 的覆蓋錯誤。

 

    緩存數據的批量操作無法充分支持,如 mset / mget 並不能直接操作到所有對應 key中,除非是具有相同 slot。 這是由於 Sharding機制原理決定的,舉一反三,若是存在事務操作,也存在相關的限制(另外稍微註意,截止目前,不同node 本身也是無法事務關聯的)。


  2.3 Redis Cluster的相關細節考慮

 

    對於 Redis node 的數量 N 理論上需要保持偶數台,一般不少於6個才能保證組成一個閉環的高可用的集群,這也意味著理想狀態至少需要6台伺服器來承載,但這裡個人在架構設計中,往往場景不是很敏感,那麼將設計為 N/2台伺服器分佈,目的是兼顧成本以及折中照顧到主從之間的 HA機制(HA相關可以參照上一篇里提到的部分延伸,這裡儘量避免重覆性討論)。 這裡要稍微註意的是,對於機器的配對,儘量保證不要在同一臺機器上配置過多的Master,否則會嚴重影響選舉,甚至failover被直接拒絕,無法重建。

 

    對於 node 之間的消息交互,每次發送的數據均包含 slot數據和集群基礎狀態,node越多分發的數據也幾近是倍增,這個在上面 Sharding 演算法里也表明,那麼一方面可以針對 node數量進行控制,另一個則是設置合理的消息發送頻率,比如在主要配置 cluster-node-timeout上,適當由預設15秒遞增 5/10 秒。但是過度調大 cluster_node_timeout相關設置一定會影響到消息交換的實時性,所以我認為這裡可以嘗試微調,在大多數本身比較均勻分佈數據的場景下適當放寬,這樣不會對node檢測和選舉產生較大影響,同時也間接節約了一定網路IO。

 

    對於數據增長粒度較大的場景,優先控制集群的 node數量,否則同樣避免不了一個比較大頭的用戶指令消耗 和 Gossip維護消息開銷(集群內所有 node的ping/pong消息),官方早前就建議控制集群規模不是沒有道理的。個人認為真到了需要的場景,必須主動作減法,縮減node數,取而代之使用小的集群來分散業務,而且這也有利於更精確的控制風險和針對性優化。

    對於集群的伸縮,如項目中應用較多操作的一般是擴容場景,增加新的node 建議跟集群內的已有 node 配置保持一致,並且在完成 cluster meet 後,需要合理控制劃分出的 slot 數,一般沒有特殊要求,應該都是均勻化。額外要稍微註意的是,新的 node 務必保證是一個乾凈的node,否則會造成不必要的拓撲錯誤(這種是可能會導致數據分佈複製嚴重錯亂的),當然新增 node這裡也可以藉助 redis-trib.rb 或者其他第三方包裝的方案來輔助操作。

    

    對於不在當前 node 的鍵指令查詢,預設是只回覆重定向轉移響應(redirect / moved)給到調用的客戶端(這裡特指應用程式端),並不負責轉發。這是跟單機是完全不同的,所以即使是使用相關的第三方驅動庫(比如JAVA的Jedis、和.Net的 StackExchange.Redis)完成程式端的封閉式控制,也仍舊需要權衡數據的熱點分散是否足夠集中在各自的node中等細節。當然,假如是 hashset等結構,由於Cluster本身的Sharding機制涉及到不可分散負載,倒是無需過多編碼實現,也不用擔心性能在這裡的損耗。

 


結語

  本篇先寫到這裡,下一篇會繼續圍繞相關主題嘗試擴展闡述。
  PS:由於個人能力和經驗均有限,自己也在持續學習和實踐,文中若有不妥之處,懇請指正。

  個人目前備用地址:
    社區1:https://yq.aliyun.com/u/autumnbing
    社區2:https://www.cnblogs.com/bsfz/

【預留占位:分散式系統之緩存的微觀應用經驗談(四)【交互場景篇】

 



End.

 

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 前言: 在生產環境中,未避免單點故障,每個微服務都會做高可用部署。 通白的說,就是每一個一模一樣的服務會根據需求提供多分在多台機器上。 那麼在大併發的情況下,如何分配服務可以快速得到響應,就成為了我們要解決的問題。 Ribbon就是一款優秀的客戶端負載均衡機制。 什麼是客戶端負載均衡呢? 就是由服務 ...
  • 本文探討如下幾個問題: 什麼是架構屬性 約束和架構屬性的關係 有哪些架構屬性 各個架構屬性涉及知識點 什麼是架構屬性 首先,問個很簡單的問題!請看下麵的Java代碼: 請問上面的代碼中: name和age被稱為Person這個類的什麼? skill又稱為Person這個類的什麼呢? name和age ...
  • 文章提綱 概述要點 理論基礎 詳細步驟 總結 概述要點 理論基礎 詳細步驟 總結 概述要點 設計模式的產生,就是在對開發過程進行不斷的抽象。 我們先看一下之前訪問數據的典型過程。 在Controller中定義一個Context, 例如: private AccountContext db = new ...
  • java併發的一系列框架和技術主要是由java.util.concurrent 包所提供。包下的所有類可以分為如下幾大類: locks部分:顯式鎖(互斥鎖和速寫鎖)相關; atomic部分:原子變數類相關,是構建非阻塞演算法的基礎; executor部分:線程池相關; collections部分:併發 ...
  • 真值和假值 相等操作符(==和 ) 下麵分析一下不同類型的值用相等操作符(==)比較後的結果 toNumber 對不同 類型返回的結果如下: toPrimitive 對不同類型返回的結果如下: 操作符。如果比較的兩個值的類型相同,結果就如下;如果比較的兩個值類型不同,返回的就是false 下麵的例子 ...
  • 關於MVC架構中的Repository模式 關於MVC架構中的Repository模式 關於MVC架構中的Repository模式 關於MVC架構中的Repository模式 個人理解:Repository是一個獨立的層,介於領域層與數據映射層(數據訪問層)之間。它的存在讓領域層感覺不到數據訪問層的 ...
  • 本文長度為3032字,預計讀完需1.1MB流量,建議閱讀8分鐘。 閱讀目錄 為什麼沒有DNS? 如何實施? 優缺點 結語 閱讀目錄 為什麼沒有DNS? 如何實施? 優缺點 結語 為什麼沒有DNS? 如何實施? 優缺點 結語 為什麼沒有DNS? 如何實施? 優缺點 結語 為什麼沒有DNS? 如何實施? ...
  • 領域驅動的火爆程度不用我贅述,但是即便其如此得耳熟能詳,但大多數人對其的認識,還只是停留在知道它的縮寫是DDD,知道它是一種軟體思想,或者知道它和微服務有千絲萬縷的關係。Eric Evans對DDD的詮釋是那麼地惜字如金,而我所認識的領域驅動設計的專家又都是行業中的資深前輩,他們擅長於對軟體設計進行 ...
一周排行
    -Advertisement-
    Play Games
  • 前言 本文介紹一款使用 C# 與 WPF 開發的音頻播放器,其界面簡潔大方,操作體驗流暢。該播放器支持多種音頻格式(如 MP4、WMA、OGG、FLAC 等),並具備標記、實時歌詞顯示等功能。 另外,還支持換膚及多語言(中英文)切換。核心音頻處理採用 FFmpeg 組件,獲得了廣泛認可,目前 Git ...
  • OAuth2.0授權驗證-gitee授權碼模式 本文主要介紹如何筆者自己是如何使用gitee提供的OAuth2.0協議完成授權驗證並登錄到自己的系統,完整模式如圖 1、創建應用 打開gitee個人中心->第三方應用->創建應用 創建應用後在我的應用界面,查看已創建應用的Client ID和Clien ...
  • 解決了這個問題:《winForm下,fastReport.net 從.net framework 升級到.net5遇到的錯誤“Operation is not supported on this platform.”》 本文內容轉載自:https://www.fcnsoft.com/Home/Sho ...
  • 國內文章 WPF 從裸 Win 32 的 WM_Pointer 消息獲取觸摸點繪製筆跡 https://www.cnblogs.com/lindexi/p/18390983 本文將告訴大家如何在 WPF 裡面,接收裸 Win 32 的 WM_Pointer 消息,從消息裡面獲取觸摸點信息,使用觸摸點 ...
  • 前言 給大家推薦一個專為新零售快消行業打造了一套高效的進銷存管理系統。 系統不僅具備強大的庫存管理功能,還集成了高性能的輕量級 POS 解決方案,確保頁面載入速度極快,提供良好的用戶體驗。 項目介紹 Dorisoy.POS 是一款基於 .NET 7 和 Angular 4 開發的新零售快消進銷存管理 ...
  • ABP CLI常用的代碼分享 一、確保環境配置正確 安裝.NET CLI: ABP CLI是基於.NET Core或.NET 5/6/7等更高版本構建的,因此首先需要在你的開發環境中安裝.NET CLI。這可以通過訪問Microsoft官網下載並安裝相應版本的.NET SDK來實現。 安裝ABP ...
  • 問題 問題是這樣的:第三方的webapi,需要先調用登陸介面獲取Cookie,訪問其它介面時攜帶Cookie信息。 但使用HttpClient類調用登陸介面,返回的Headers中沒有找到Cookie信息。 分析 首先,使用Postman測試該登陸介面,正常返回Cookie信息,說明是HttpCli ...
  • 國內文章 關於.NET在中國為什麼工資低的分析 https://www.cnblogs.com/thinkingmore/p/18406244 .NET在中國開發者的薪資偏低,主要因市場需求、技術棧選擇和企業文化等因素所致。歷史上,.NET曾因微軟的閉源策略發展受限,儘管後來推出了跨平臺的.NET ...
  • 在WPF開發應用中,動畫不僅可以引起用戶的註意與興趣,而且還使軟體更加便於使用。前面幾篇文章講解了畫筆(Brush),形狀(Shape),幾何圖形(Geometry),變換(Transform)等相關內容,今天繼續講解動畫相關內容和知識點,僅供學習分享使用,如有不足之處,還請指正。 ...
  • 什麼是委托? 委托可以說是把一個方法代入另一個方法執行,相當於指向函數的指針;事件就相當於保存委托的數組; 1.實例化委托的方式: 方式1:通過new創建實例: public delegate void ShowDelegate(); 或者 public delegate string ShowDe ...