高可用Redis服務架構分析與搭建

来源:https://www.cnblogs.com/xuning/archive/2018/02/24/8464625.html
-Advertisement-
Play Games

基於記憶體的Redis應該是目前各種web開發業務中最為常用的key-value資料庫了,我們經常在業務中用其存儲用戶登陸態(Session存儲),加速一些熱數據的查詢(相比較mysql而言,速度有數量級的提升),做簡單的消息隊列(LPUSH和BRPOP)、訂閱發佈(PUB/SUB)系統等等。規模比較 ...


基於記憶體的Redis應該是目前各種web開發業務中最為常用的key-value資料庫了,我們經常在業務中用其存儲用戶登陸態(Session存儲),加速一些熱數據的查詢(相比較mysql而言,速度有數量級的提升),做簡單的消息隊列(LPUSH和BRPOP)、訂閱發佈(PUB/SUB)系統等等。規模比較大的互聯網公司,一般都會有專門的團隊,將Redis存儲以基礎服務的形式提供給各個業務調用。

不過任何一個基礎服務的提供方,都會被調用方問起的一個問題是:你的服務是否具有高可用性?最好不要因為你的服務經常出問題,導致我這邊的業務跟著遭殃。最近我所在的項目中也自己搭了一套小型的“高可用”Redis服務,在此做一下自己的總結和思考。

首先我們要定義一下對於Redis服務來說怎樣才算是高可用,即在各種出現異常的情況下,依然可以正常提供服務。或者寬鬆一些,出現異常的情況下,只經過很短暫的時間即可恢復正常服務。所謂異常,應該至少包含了以下幾種可能性:

【異常1】某個節點伺服器的某個進程突然down掉(例如某開發手殘,把一臺伺服器的redis-server進程kill了)

【異常2】某台節點伺服器down掉,相當於這個節點上所有進程都停了(例如某運維手殘,把一個伺服器的電源拔了;例如一些老舊機器出現硬體故障)

【異常3】任意兩個節點伺服器之間的通信中斷了(例如某臨時工手殘,把用於兩個機房通信的光纜挖斷了)

其實以上任意一種異常都是小概率事件,而做到高可用性的基本指導思想就是:多個小概率事件同時發生的概率可以忽略不計。只要我們設計的系統可以容忍短時間內的單點故障,即可實現高可用性。

對於搭建高可用Redis服務,網上已有了很多方案,例如Keepalived,Codis,Twemproxy,Redis Sentinel。其中Codis和Twemproxy主要是用於大規模的Redis集群中,也是在Redis官方發佈Redis Sentinel之前twitter和豌豆莢提供的開源解決方案。我的業務中數據量並不大,所以搞集群服務反而是浪費機器了。最終在Keepalived和Redis Sentinel之間做了個選擇,選擇了官方的解決方案Redis Sentinel。

Redis Sentinel可以理解為一個監控Redis Server服務是否正常的進程,並且一旦檢測到不正常,可以自動地將備份(slave)Redis Server啟用,使得外部用戶對Redis服務內部出現的異常無感知。我們按照由簡至繁的步驟,搭建一個最小型的高可用的Redis服務。

 

方案1:單機版Redis Server,無Sentinel

一般情況下,我們搭的個人網站,或者平時做開發時,會起一個單實例的Redis Server。調用方直接連接Redis服務即可,甚至Client和Redis本身就處於同一臺伺服器上。這種搭配僅適合個人學習娛樂,畢竟這種配置總會有單點故障的問題無法解決。一旦Redis服務進程掛了,或者伺服器1停機了,那麼服務就不可用了。並且如果沒有配置Redis數據持久化的話,Redis內部已經存儲的數據也會丟失。

 

方案2:主從同步Redis Server,單實例Sentinel

為了實現高可用,解決方案1中所述的單點故障問題,我們必須增加一個備份服務,即在兩台伺服器上分別各啟動一個Redis Server進程,一般情況下由master提供服務,slave只負責同步和備份。與此同時,在額外啟動一個Sentinel進程,監控兩個Redis Server實例的可用性,以便在master掛掉的時候,及時把slave提升到master的角色繼續提供服務,這樣就實現了Redis Server的高可用。這基於一個高可用服務設計的依據,即單點故障本身就是個小概率事件,而多個單點同時故障(即master和slave同時掛掉),可以認為是(基本)不可能發生的事件。

對於Redis服務的調用方來說,現在要連接的是Redis Sentinel服務,而不是Redis Server了。常見的調用過程是,client先連接Redis Sentinel並詢問目前Redis Server中哪個服務是master,哪些是slave,然後再去連接相應的Redis Server進行操作。當然目前的第三方庫一般都已經實現了這一調用過程,不再需要我們手動去實現(例如Nodejs的ioredis,PHP的predis,Golang的go-redis/redis,JAVA的jedis等)。

然而,我們實現了Redis Server服務的主從切換之後,又引入了一個新的問題,即Redis Sentinel本身也是個單點服務,一旦Sentinel進程掛了,那麼客戶端就沒辦法鏈接Sentinel了。所以說,方案2的配置並無法實現高可用性。

 

方案3:主從同步Redis Server,雙實例Sentinel

為瞭解決方案2的問題,我們把Redis Sentinel進程也額外啟動一份,兩個Sentinel進程同時為客戶端提供服務發現的功能。對於客戶端來說,它可以連接任何一個Redis Sentinel服務,來獲取當前Redis Server實例的基本信息。通常情況下,我們會在Client端配置多個Redis Sentinel的鏈接地址,Client一旦發現某個地址連接不上,會去試圖連接其他的Sentinel實例,這當然也不需要我們手動實現,各個開發語言中比較熱門的redis連接庫都幫我們實現了這個功能。我們預期是:即使其中一個Redis Sentinel掛掉了,還有另外一個Sentinel可以提供服務。

然而,願景是美好的,現實卻是很殘酷的。如此架構下,依然無法實現Redis服務的高可用。方案3示意圖中,紅線部分是兩台伺服器之間的通信,而我們所設想的異常場景(【異常2】)是,某台伺服器整體down機,不妨假設伺服器1停機,此時,只剩下伺服器2上面的Redis Sentinel和slave Redis Server進程。這時,Sentinel其實是不會將僅剩的slave切換成master繼續服務的,也就導致Redis服務不可用,因為Redis的設定是只有當超過50%的Sentinel進程可以連通並投票選取新的master時,才會真正發生主從切換。本例中兩個Sentinel只有一個可以連通,等於50%並不在可以主從切換的場景中。

你可能會問,為什麼Redis要有這個50%的設定?假設我們允許小於等於50%的Sentinel連通的場景下也可以進行主從切換。試想一下【異常3】,即伺服器1和伺服器2之間的網路中斷,但是伺服器本身是可以運行的。如下圖所示:

實際上對於伺服器2來說,伺服器1直接down掉和伺服器1網路連不通是一樣的效果,反正都是突然就無法進行任何通信了。假設網路中斷時我們允許伺服器2的Sentinel把slave切換為master,結果就是你現在擁有了兩個可以對外提供服務的Redis Server。Client做任何的增刪改操作,有可能落在伺服器1的Redis上,也有可能落在伺服器2的Redis上(取決於Client到底連通的是哪個Sentinel),造成數據混亂。即使後面伺服器1和伺服器2之間的網路又恢復了,那我們也無法把數據統一了(兩份不一樣的數據,到底該信任誰呢?),數據一致性完全被破壞。

 

方案4:主從同步Redis Server,三實例Sentinel

鑒於方案3並沒有辦法做到高可用,我們最終的版本就是上圖所示的方案4了。實際上這就是我們最終搭建的架構。我們引入了伺服器3,並且在3上面又搭建起一個Redis Sentinel進程,現在由三個Sentinel進程來管理兩個Redis Server實例。這種場景下,不管是單一進程故障、還是單個機器故障、還是某兩個機器網路通信故障,都可以繼續對外提供Redis服務。

實際上,如果你的機器比較空閑,當然也可以把伺服器3上面也開啟一個Redis Server,形成1 master + 2 slave的架構,每個數據都有兩個備份,可用性會提升一些。當然也並不是slave越多越好,畢竟主從同步也是需要時間成本的。

在方案4中,一旦伺服器1和其他伺服器的通信完全中斷,那麼伺服器2和3會將slave切換為master。對於客戶端來說,在這麼一瞬間會有2個master提供服務,並且一旦網路恢復了,那麼所有在中斷期間落在伺服器1上的新數據都會丟失。如果想要部分解決這個問題,可以配置Redis Server進程,讓其在檢測到自己網路有問題的時候,立即停止服務,避免在網路故障期間還有新數據進來(可以參考Redis的min-slaves-to-write和min-slaves-max-lag這兩個配置項)。

至此,我們就用3台機器搭建了一個高可用的Redis服務。其實網上還有更加節省機器的辦法,就是把一個Sentinel進程放在Client機器上,而不是服務提供方的機器上。只不過在公司裡面,一般服務的提供方和調用方並不來自同一個團隊。兩個團隊共同操作同一個機器,很容易因為溝通問題導致一些誤操作,所以出於這種人為因素的考慮,我們還是採用了方案4的架構。並且由於伺服器3上面只跑了一個Sentinel進程,對伺服器資源消耗並不多,還可以用伺服器3來跑一些其他的服務。

 

易用性:像使用單機版Redis一樣使用Redis Sentinel

作為服務的提供方,我們總是會講到用戶體驗問題。在上述方案當中始終有一個讓Client端用的不是那麼舒服的地方。對於單機版Redis,Client端直接連接Redis Server,我們只需要給一個ip和port,Client就可以使用我們的服務了。而改造成Sentinel模式之後,Client不得不採用一些支持Sentinel模式的外部依賴包,並且還要修改自己的Redis連接配置,這對於“矯情”的用戶來講顯然是不能接收的。有沒有辦法還是像在使用單機版的Redis那樣,只給Client一個固定的ip和port就可以提供服務呢?

答案當然是肯定的。這可能就要引入虛擬IP(Virtual IP,VIP),如上圖所示。我們可以把虛擬IP指向Redis Server master所在的伺服器,在發生Redis主從切換的時候,會觸發一個回調腳本,回調腳本中將VIP切換至slave所在的伺服器。這樣對於Client端來說,他仿佛在使用的依然是一個單機版的高可用Redis服務。

 

結語

搭建任何一個服務,做到“能用”其實是非常簡單的,就像我們運行一個單機版的Redis。不過一旦要做到“高可用”,事情就會變得複雜起來。業務中使用了額外的兩台伺服器,3個Sentinel進程+1個Slave進程,只是為了保證在那小概率的事故中依然做到服務可用。在實際業務中我們還啟用了supervisor做進程監控,一旦進程意外退出,會自動嘗試重新啟動。


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

-Advertisement-
Play Games
更多相關文章
  • ROW_NUMBER()函數將針對SELECT語句返回的每一行,從1開始編號,賦予其連續的編號 必須和over一起使用 select *,ROW_NUMBER() over(order by productid) as num from product_test; ...
  • 1增加主機映射(與namenode的映射一樣): 增加最後一行 2新建用戶hadoop 建立hadoop用戶組 新建用戶,useradd -d /usr/hadoop -g hadoop -m hadoop (新建用戶hadoop指定用戶主目錄/usr/hadoop 及所屬組hadoop) pass ...
  • 單個 節點 可以作為一個運行中的 Elasticsearch 的實例。 而一個 集群 是一組擁有相同 cluster.name 的節點, 他們能一起工作並共用數據,還提供容錯與可伸縮性。(當然,一個單獨的節點也可以組成一個集群) 你可以在 elasticsearch.yml 配置文件中 修改 clu... ...
  • ceiling函數返回大於或等於所給數字表達式的最小整數。 floor函數返回小於或等於所給數字表達式的最大整數。 eg: select ceiling(4.42) 5select CEILING(0.1) 1 select FLOOR(0.1)--0select FLOOR(4.42) 4 註意: ...
  • 索引 其實資料庫中的數據是按頁存放的其實索引也是按頁存放的所以本質上索引也占硬碟空間(以最小的消耗,換取最大的利益) 索引是一種有效組合數據的方式!為快速查找到指定記錄做鋪墊 目的就是快速或者某個記錄! 提高了資料庫的檢索速度!作用:大大提高資料庫的檢索速度改善資料庫性能 MySQL索引存儲類型分類 ...
  • 鎖相容性圖: 一、鎖的粒度: 比較需要註意的是RID/KEY、HoBT/PAGE這兩對兒的區別,RID和HoBT是針對堆表的,即沒有聚集索引的表。 二、鎖的模式: 1.關於其中的S、U、X鎖: 共用鎖 共用鎖(S 鎖)允許併發事務在封閉式併發控制下讀取 (SELECT) 資源。 資源上存在共用鎖(S ...
  • 存儲常式是存儲在資料庫伺服器中的一組sql語句,通過在查詢中調用一個指定的名稱來執行這些sql語句命令. 簡介 SQL語句需要先編譯然後執行,而存儲過程(Stored Procedure)是一組為了完成特定功能的SQL語句集,經編譯後存儲在資料庫中,用戶通過指定存儲過程的名字並給定參數(如果該存儲過 ...
  • Hadoop集群部署,就是以Cluster mode方式進行部署。本文是基於JDK1.7.0_79,hadoop2.7.5。 1.Hadoop的節點構成如下: HDFS daemon: NameNode, SecondaryNameNode, DataNode YARN damones: Resou ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...