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

来源: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
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...