基於記憶體的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做進程監控,一旦進程意外退出,就會自動嘗試重新啟動。
推薦學習:Redis影片教學
以上是高可用Redis服務架構分析與搭建的詳細內容。更多資訊請關注PHP中文網其他相關文章!

Redis是现在最热门的key-value数据库,Redis的最大特点是key-value存储所带来的简单和高性能;相较于MongoDB和Redis,晚一年发布的ES可能知名度要低一些,ES的特点是搜索,ES是围绕搜索设计的。

本篇文章给大家带来了关于redis的相关知识,其中主要介绍了关于redis的一些优势和特点,Redis 是一个开源的使用ANSI C语言编写、遵守 BSD 协议、支持网络、可基于内存、分布式存储数据库,下面一起来看一下,希望对大家有帮助。

本篇文章给大家带来了关于redis的相关知识,其中主要介绍了Redis Cluster集群收缩主从节点的相关问题,包括了Cluster集群收缩概念、将6390主节点从集群中收缩、验证数据迁移过程是否导致数据异常等,希望对大家有帮助。

本篇文章给大家带来了关于redis的相关知识,其中主要介绍了Redis实现排行榜及相同积分按时间排序,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,希望对大家有帮助。

本篇文章给大家带来了关于redis的相关知识,其中主要介绍了关于原子操作中命令原子性的相关问题,包括了处理并发的方案、编程模型、多IO线程以及单命令的相关内容,下面一起看一下,希望对大家有帮助。

本篇文章给大家带来了关于redis的相关知识,其中主要介绍了bitmap问题,Redis 为我们提供了位图这一数据结构,位图数据结构其实并不是一个全新的玩意,我们可以简单的认为就是个数组,只是里面的内容只能为0或1而已,希望对大家有帮助。

本篇文章给大家带来了关于redis的相关知识,其中主要介绍了Redis实现排行榜及相同积分按时间排序,本文通过实例代码给大家介绍的非常详细,下面一起来看一下,希望对大家有帮助。

redis error就是redis数据库和其组合使用的部件出现错误,这个出现的错误有很多种,例如Redis被配置为保存数据库快照,但它不能持久化到硬盘,用来修改集合数据的命令不能用。


熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

AI Hentai Generator
免費產生 AI 無盡。

熱門文章

熱工具

ZendStudio 13.5.1 Mac
強大的PHP整合開發環境

EditPlus 中文破解版
體積小,語法高亮,不支援程式碼提示功能

MantisBT
Mantis是一個易於部署的基於Web的缺陷追蹤工具,用於幫助產品缺陷追蹤。它需要PHP、MySQL和一個Web伺服器。請查看我們的演示和託管服務。

SublimeText3 Linux新版
SublimeText3 Linux最新版

mPDF
mPDF是一個PHP庫,可以從UTF-8編碼的HTML產生PDF檔案。原作者Ian Back編寫mPDF以從他的網站上「即時」輸出PDF文件,並處理不同的語言。與原始腳本如HTML2FPDF相比,它的速度較慢,並且在使用Unicode字體時產生的檔案較大,但支援CSS樣式等,並進行了大量增強。支援幾乎所有語言,包括RTL(阿拉伯語和希伯來語)和CJK(中日韓)。支援嵌套的區塊級元素(如P、DIV),