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

2022-09-24 02:00:10 字數 4776 閱讀 5664

基於記憶體的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服務是否正常的程序,並且一旦檢測到不正常,可以自動地將備份(sl**e)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提供服務,sl**e只負責同步和備份。

與此同時,在額外啟動乙個sentinel程序,監控兩個redis server例項的可用性,以便在master掛掉的時候,及時把sl**e提公升到master的角色繼續提供服務,這樣就實現了redis server的高可用。

這基於乙個高可用服務設計的依據,即單點故障本身就是個小概率事件,而多個單點同時故障(即master和sl**e同時掛掉),可以認為是(基本)不可能發生的事件。

對於redis服務的呼叫方來說,現在要連線的是redis sentinel服務,而不是redis server了。常見的呼叫過程是,client先連線redis sentinel並詢問目前redis server中哪個服務是master,哪些是sl**e,然後再去連線相應的redis server進行操作。

當然目前的第三方庫一般都已經實現了這一呼叫過程,不再需要我們手動去實現(例如nodejs的ioredis,php的predis,golang的go-redis/redis,j**a的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和sl**e redis server程序。

這時,sentinel其實是不會將僅剩的sl**e切換成master繼續服務的,也就導致redis服務不可用,因為redis的設定是只有當超過50%的sentinel程序可以連通並投票選取新的master時,才會真正發生主從切換。本例中兩個sentinel只有乙個可以連通,等於50%並不在可以主從切換的場景中。

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

實際上對於伺服器2來說,伺服器1直接down掉和伺服器1網路連不通是一樣的效果,反正都是突然就無法進行任何通訊了。假設網路中斷時我們允許伺服器2的sentinel把sl**e切換為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 sl**e的架構,每個資料都有兩個備份,可用性會提公升一些。當然也並不是sl**e越多越好,畢竟主從同步也是需要時間成本的。

在方案4中,一旦伺服器1和其他伺服器的通訊完全中斷,那麼伺服器2和3會將sl**e切換為master。對於客戶端來說,在這麼一瞬間會有2個master提供服務,並且一旦網路恢復了,那麼所有在中斷期間落在伺服器1上的新資料都會丟失。如果想要部分解決這個問題,可以配置redis server程序,讓其在檢測到自己網路有問題的時候,立即停止服務,避免在網路故障期間還有新資料進來(可以參考redis的min-sl**es-to-write和min-sl**es-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切換至sl**e所在的伺服器。這樣對於client端來說,他彷彿在使用的依然是乙個單機版的高可用redis服務。

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

Redis高可用架構

官網 解壓 tar zxvf redis 5.0.5.tar.gz 切換目錄 cd redis 3.2.9,執行編譯命令 make 切換到 redis 3.2.9 src 目錄執行命令 vim redis.conf protected mode no bind 127.0.0.1 daemonize...

redis哨兵高可用架構

哨兵架構相關基礎知識 哨兵介紹 sentinal 哨兵 哨兵是redis集群架構中非常重要的乙個元件,主要功能如下 哨兵本身也是分布式的,作為乙個哨兵集群去執行,互相協同工作。故障發生轉移時,判斷乙個master node是宕機了,需要大部分的哨兵同意才行,涉及到了分布式選舉的問題 即使部分哨兵節點...

架構高可用 服務保護

服務降級 限流是服務降級的一種,限制系統輸出和輸入流量從而保護系統。系統吞吐量是一定的,可以通過壓力測試得到。有可能會超過系統閾值,為了保證系統的穩定,需要採取一些措施,比如,延遲處理,拒絕處理,部分拒絕處理等 計數器優勢 控制單位時間內的請求數量,簡單粗暴 劣勢 無法應對極短時間裡的突發流量 滑動...