Redis集群·Redis集群提供了一種運(yùn)行Redis安裝的方法,在該安裝中,數(shù)據(jù)會(huì)在多個(gè)Redis節(jié)點(diǎn)之間自動(dòng)分片。 Redis集群在分區(qū)期間還提供了一定程度的可用性,這實(shí)際上是在某些節(jié)點(diǎn)出現(xiàn)故障或無法通信時(shí)有繼續(xù)工作的能力。但是,如果發(fā)生較嚴(yán)重故障(例如,大多數(shù)主節(jié)點(diǎn)不可用時(shí)),集群將停止運(yùn)行。 實(shí)際上,Redis集群能給你帶來什么?
Redis集群TCP端口每個(gè)Redis集群節(jié)點(diǎn)都需要打開兩個(gè)TCP連接。用于服務(wù)客戶端的常規(guī)RedisTCP端口,例如 第二個(gè)值更大一點(diǎn)的端口用于集群總線,也就是使用二進(jìn)制協(xié)議的節(jié)點(diǎn)到節(jié)點(diǎn)之間的通信通道。節(jié)點(diǎn)將集群總線用于故障檢測,配置更新,故障轉(zhuǎn)移授權(quán)等??蛻舳擞肋h(yuǎn)不要嘗試與集群總線端口進(jìn)行通信,而應(yīng)始終與普通的Redis命令端口進(jìn)行通信,但是請(qǐng)確保您在防火墻中同時(shí)打開了這兩個(gè)端口,否則Redis集群節(jié)點(diǎn)將無法進(jìn)行通信。 命令端口和集群總線端口的偏移量是固定的,并且始終為 請(qǐng)注意,對(duì)于每個(gè)節(jié)點(diǎn),要使Redis集群正常工作,您需要:
如果您沒有同時(shí)打開兩個(gè)TCP端口,則集群將無法正常工作。 集群總線使用不同的二進(jìn)制協(xié)議進(jìn)行節(jié)點(diǎn)到節(jié)點(diǎn)的數(shù)據(jù)交換,它更適合于在節(jié)點(diǎn)之間使用較少的帶寬和較少的處理時(shí)間來交換信息。 Redis集群數(shù)據(jù)分片Redis集群不使用一致性哈希,而是使用一種不同形式的分片,從概念上講每個(gè)key都是我們稱為hash槽的一部分。 Redis集群中有16384個(gè)hash槽,要計(jì)算給定key的hash槽,需將key的CRC16值用16384取模。 Redis集群中的每個(gè)節(jié)點(diǎn)都負(fù)責(zé)hash槽的子集,例如,您可能有一個(gè)包含3個(gè)節(jié)點(diǎn)的集群,其中:
這樣可以輕松添加和刪除集群中的節(jié)點(diǎn)。例如,如果我想添加一個(gè)新節(jié)點(diǎn)D,則需要將一些hash槽從節(jié)點(diǎn)A,B,C移到D。類似地,如果我想從集群中刪除節(jié)點(diǎn)A,則只需移動(dòng)A所服務(wù)的hash槽到B和C。當(dāng)節(jié)點(diǎn)A為空時(shí),我可以將其從集群中完全刪除。 因?yàn)閷ash槽從一個(gè)節(jié)點(diǎn)移動(dòng)到另一個(gè)節(jié)點(diǎn)不需要停止操作,所以添加刪除節(jié)點(diǎn)或更改節(jié)點(diǎn)服務(wù)的hash槽的百分比不需要停機(jī)。 只要單個(gè)命令執(zhí)行(或整個(gè)事務(wù)或Lua腳本執(zhí)行)中涉及的所有key都屬于同一個(gè)hash槽,Redis集群就支持多key操作。用戶可以通過使用稱為hash標(biāo)簽的概念來強(qiáng)制多個(gè)key成為同一hash槽的一部分。 hash標(biāo)簽記錄在Redis集群規(guī)范中,注意,如果key的{}中的括號(hào)之間有一個(gè)子字符串,則僅對(duì)字符串中的內(nèi)容進(jìn)行hash處理,例如,一個(gè)叫{foo}的key和另一個(gè)叫{foo} 的 key保證在同一hash槽中,并且可以在以多個(gè)key作為參數(shù)的命令中一起使用。 Redis集群主備模式為了在主節(jié)點(diǎn)子集發(fā)生故障或無法與大多數(shù)節(jié)點(diǎn)通信時(shí)保持可用,Redis集群使用主備模型,其中每個(gè)hash槽具有從1(主節(jié)點(diǎn)本身)到N個(gè)副本(N -1個(gè)其他備份節(jié)點(diǎn))。 在一個(gè)包含節(jié)點(diǎn)A,B,C的集群中,如果節(jié)點(diǎn)B失敗,則集群將無法繼續(xù),因?yàn)槲覀儾荒転?5501-11000 范圍內(nèi)的hash槽提供服務(wù)。但是,在創(chuàng)建集群(或稍后)時(shí),我們向每個(gè)主節(jié)點(diǎn)添加一個(gè)備份節(jié)點(diǎn),以便最終集群由作為主節(jié)點(diǎn)的A,B,C和作為備份節(jié)點(diǎn)的A1,B1,C1組成 ,如果節(jié)點(diǎn)B發(fā)生故障,系統(tǒng)將能夠持續(xù)運(yùn)行。節(jié)點(diǎn)B1復(fù)制B,并且B發(fā)生故障,集群會(huì)將節(jié)點(diǎn)B1提升為新的主節(jié)點(diǎn),并將繼續(xù)正常運(yùn)行。
但是請(qǐng)注意,如果節(jié)點(diǎn)B和B1同時(shí)失敗,則Redis集群無法繼續(xù)運(yùn)行。 Redis集群的一致性保證Redis集群無法保證強(qiáng)一致性。 實(shí)際上,這意味著在某些情況下,Redis集群可能會(huì)丟失系統(tǒng)給客戶端的已經(jīng)確認(rèn)的寫操作。 Redis集群可能丟失寫入的第一個(gè)原因是因?yàn)樗褂卯惒綇?fù)制。這意味著在寫入期
如您所見,B在回復(fù)客戶端之前不會(huì)等待B1,B2,B3的確認(rèn),因?yàn)檫@會(huì)對(duì)Redis造成延遲,因此,如果您的客戶端進(jìn)行了寫操作,然后B會(huì)確認(rèn),但是在它把寫操作發(fā)送給備份節(jié)點(diǎn)之前崩潰了,此時(shí)其中一個(gè)備份節(jié)點(diǎn)(未接收到寫操作)可以升級(jí)為主節(jié)點(diǎn),這樣就永遠(yuǎn)丟失該寫操作。這與配置為每秒將數(shù)據(jù)刷新到磁盤的大多數(shù)數(shù)據(jù)庫所發(fā)生的情況非常相似,因此由于過去使用不涉及分布式系統(tǒng)的傳統(tǒng)數(shù)據(jù)庫系統(tǒng)的經(jīng)驗(yàn),您已經(jīng)可以對(duì)此進(jìn)行合理推斷。同樣,您可以通過強(qiáng)制數(shù)據(jù)庫在答復(fù)客戶端之前刷新磁盤上的數(shù)據(jù)來提高一致性,但這通常會(huì)導(dǎo)致性能過低。在Redis集群下,這相當(dāng)于同步復(fù)制。 基本上,在性能和一致性之間進(jìn)行權(quán)衡是必須的。 Redis集群在需要時(shí)可以通過WAIT命令實(shí)現(xiàn)同步寫,這使得丟失寫的可能性大大降低,但是請(qǐng)注意,即使使用同步復(fù)制,Redis集群也不實(shí)現(xiàn)強(qiáng)一致性:在更復(fù)雜的情況下,總是有可能存在一種場景,就是一個(gè)無法接收數(shù)據(jù)的備份節(jié)點(diǎn)被選為主節(jié)點(diǎn)。 還有一種值得注意的情況,Redis集群也會(huì)丟失寫操作,這種情況發(fā)生在網(wǎng)絡(luò)分區(qū)期間,在該分區(qū)中,客戶端與少數(shù)實(shí)例(至少包括主節(jié)點(diǎn))隔離。 以我們的6個(gè)節(jié)點(diǎn)集群為例,該集群由A,B,C,A1,B1,C1組成,具有3個(gè)主節(jié)點(diǎn) 發(fā)生分區(qū)后,可能在分區(qū)的一側(cè)有A,C,A1,B1,C1,而在另一側(cè)有B和Z1。 Z1仍然能夠?qū)進(jìn)行寫操作,B將接受其寫入。如果分區(qū)在很短的時(shí)間內(nèi)恢復(fù)正常,則集群將繼續(xù)正常運(yùn)行。但是,如果分區(qū)持續(xù)的時(shí)間足以使B1升級(jí)為該分區(qū)的多數(shù)端的主節(jié)點(diǎn),則Z1向B發(fā)送的寫操作將丟失。 請(qǐng)注意,Z1將能夠發(fā)送到B的寫入量有一個(gè)最大的窗口:如果已經(jīng)有足夠的時(shí)間使大分區(qū)選舉出一個(gè)主節(jié)點(diǎn),則小分區(qū)中的每個(gè)主節(jié)點(diǎn)都將停止接受寫入。該時(shí)間是Redis集群的一個(gè)非常重要的配置指令,稱為節(jié)點(diǎn)超時(shí)。 在節(jié)點(diǎn)超時(shí)之后,主節(jié)點(diǎn)被視為發(fā)生故障,并且可以用其副本之一替換。類似地,在超過指定的時(shí)間后,主節(jié)點(diǎn)還是無法感知大多數(shù)其他主節(jié)點(diǎn),此主節(jié)點(diǎn)進(jìn)入錯(cuò)誤狀態(tài)并停止接受寫入。 Redis集群配置參數(shù)我們將創(chuàng)建一個(gè)集群部署作為例子。在繼續(xù)之前,讓我們介紹一下Redis集群里的redis.conf文件中引入的配置參數(shù)。
創(chuàng)建和使用一個(gè)Redis集群注意:手動(dòng)部署Redis集群,了解其某些操作非常重要。 但是,如果要盡快建立集群并運(yùn)行,請(qǐng)?zhí)^本節(jié)和下一節(jié),直接轉(zhuǎn)到使用 create-cluster 腳本創(chuàng)建Redis集群。 要?jiǎng)?chuàng)建集群,我們需要做的第一件事就是讓一些空Redis實(shí)例運(yùn)行在集群模式下?;旧?,這意味著不能使用常規(guī)Redis實(shí)例來創(chuàng)建集群,因?yàn)樾枰渲锰厥饽J?,以便Redis實(shí)例啟用集群特定的功能和命令。 以下是最小的Redis集群配置文件:
啟用集群模式的只需要直接打開cluster-enabled命令。每個(gè)實(shí)例還包含該節(jié)點(diǎn)配置存儲(chǔ)位置的文件路徑,默認(rèn)情況下為nodes.conf。 該文件不會(huì)被人接觸。 它只是由Redis集群實(shí)例在啟動(dòng)時(shí)生成,并在需要時(shí)進(jìn)行更新。 請(qǐng)注意,按預(yù)期工作的最小集群要求至少包含三個(gè)主節(jié)點(diǎn)。 對(duì)于您的第一個(gè)測試,強(qiáng)烈建議啟動(dòng)一個(gè)包含三個(gè)主節(jié)點(diǎn)和三個(gè)備份節(jié)點(diǎn)的六個(gè)節(jié)點(diǎn)集群。為此,輸入一個(gè)新目錄并創(chuàng)建以下目錄,該目錄以我們將在給定目錄中運(yùn)行的實(shí)例的端口號(hào)命名。就像是:
在從7000到7005的每個(gè)目錄中創(chuàng)建一個(gè)redis.conf文件。作為配置文件的模板,只需使用上面的小示例,但請(qǐng)確保根據(jù)目錄名稱用正確的端口號(hào)替換端口號(hào)7000?,F(xiàn)在,將您的redis-server可執(zhí)行文件(從GitHub不穩(wěn)定分支中的最新資源編譯而來)復(fù)制到cluster-test目錄中,最后在您喜歡的終端應(yīng)用程序中打開6個(gè)終端選項(xiàng)卡。 像這樣啟動(dòng)每個(gè)實(shí)例,每個(gè)選項(xiàng)卡一個(gè):
從每個(gè)實(shí)例的日志中可以看到,由于不存在nodes.conf文件,因此每個(gè)節(jié)點(diǎn)都會(huì)為其分配一個(gè)新的ID。
該ID將由該實(shí)例永久使用,以使該實(shí)例在集群的上下文中具有唯一的名稱。每個(gè)節(jié)點(diǎn)都使用該ID而不是IP或端口記住其他每個(gè)節(jié)點(diǎn)。 IP地址和端口可能會(huì)更改,但是唯一的節(jié)點(diǎn)標(biāo)識(shí)符在節(jié)點(diǎn)的整個(gè)生命周期中都不會(huì)改變。我們將此標(biāo)識(shí)符簡稱為節(jié)點(diǎn)ID。 創(chuàng)建集群現(xiàn)在,我們有許多實(shí)例正在運(yùn)行,然后需要通過向節(jié)點(diǎn)寫入一些有意義的配置來創(chuàng)建集群。 如果您使用的是Redis 5,這很容易完成,這是因?yàn)閞edis-cli中嵌入了Redis集群命令行實(shí)用程序,我們可以使用它來創(chuàng)建新集群,檢查或重新分片現(xiàn)有集群等。 對(duì)于Redis版本3或4,有一個(gè)稱為redis-trib.rb的較老的工具,它非常相似。您可以在Redis源代碼分發(fā)的src目錄中找到它。 您需要安裝redis gem才能運(yùn)行redis-trib。
第一個(gè)示例,即集群創(chuàng)建,將在Redis 5中使用redis-cli以及在Redis 3和4中使用redis-trib來顯示。但是,接下來的所有示例都將僅使用redis-cli,因?yàn)槟梢钥吹剿麄冋Z法非常相似,您也可以使用redis-trib.rb help來獲取有關(guān)語法的信息,從而將一個(gè)命令行簡單地更改為另一命令行。 重要:請(qǐng)注意,如果需要,可以對(duì)Redis 4集群使用Redis 5 redis-cli。 要使用redis-cli為Redis 5創(chuàng)建集群,只需鍵入:
對(duì)于redis 4或者3 請(qǐng)使用redis-trib.rb工具:
此處使用的命令是create,因?yàn)槲覀円獎(jiǎng)?chuàng)建一個(gè)新集群。 選項(xiàng) 顯然,滿足我們要求的唯一設(shè)置是創(chuàng)建具有3個(gè)主節(jié)點(diǎn)和3個(gè)從節(jié)點(diǎn)的集群。 Redis-cli將為您提供配置。 鍵入yes,將接受建議的配置。集群將被配置并加入,這意味著實(shí)例將被啟動(dòng)然后彼此
意思就是說至少有一個(gè)主節(jié)點(diǎn)實(shí)例服務(wù)于16384個(gè)槽位中某一個(gè). 使用create-cluster腳本創(chuàng)建一個(gè)Redis集群如果您不想如上所述通過手動(dòng)配置和執(zhí)行單個(gè)實(shí)例來創(chuàng)建Redis集群,則可以使用更簡單的系統(tǒng)(但是您將不會(huì)學(xué)到同樣多的操作細(xì)節(jié))。 只需檢查Redis發(fā)行版中的utils / create-cluster目錄。內(nèi)部有一個(gè)名為create-cluster的腳本(名稱與包含在其中的目錄相同),它是一個(gè)簡單的bash腳本。 為了啟動(dòng)具有3個(gè)主節(jié)點(diǎn)和3個(gè)備份節(jié)點(diǎn)的6節(jié)點(diǎn)集群,只需鍵入以下命令:
在步驟2中,當(dāng)redis-cli希望您接受集群布局時(shí),回復(fù)yes。 現(xiàn)在,您可以與集群進(jìn)行交互,默認(rèn)情況下,第一個(gè)節(jié)點(diǎn)將從端口30001開始。 完成后,使用以下命令停止集群:
關(guān)于如何運(yùn)行這個(gè)腳本的更多信息,請(qǐng)閱讀目錄里的README。 集群操作到目前為止,Redis集群的問題之一是缺少客戶端庫的實(shí)現(xiàn)。 據(jù)我所知有以下實(shí)現(xiàn):
測試Redis集群的一種簡單方法是嘗試上述任何客戶端,或者僅嘗試redis-cli命令。以下是使用后者進(jìn)行交互的示例:
注意:如果使用腳本創(chuàng)建集群,則節(jié)點(diǎn)可能會(huì)偵聽不同的端口,默認(rèn)情況下從30001開始。 redis-cli的支持非?;A(chǔ),因此它始終基于以下事實(shí):Redis集群節(jié)點(diǎn)能夠?qū)⒖蛻舳酥囟ㄏ虻秸_的節(jié)點(diǎn)。一個(gè)嚴(yán)格的客戶端可以做得更好,并且可以在hash槽和節(jié)點(diǎn)地址之間緩存映射,以便直接使用與節(jié)點(diǎn)的正確連接。僅在集群配置中發(fā)生某些更改時(shí)(例如,在故障轉(zhuǎn)移之后或系統(tǒng)管理員通過添加或刪除節(jié)點(diǎn)來更改集群布局之后),才會(huì)刷新映射。 使用redis-rb-cluster寫一個(gè)簡單的應(yīng)用程序在繼續(xù)展示如何操作Redis集群之前,比如執(zhí)行故障轉(zhuǎn)移或重新分片之類的操作,我們需要?jiǎng)?chuàng)建一些示例應(yīng)用程序,或者至少要能夠理解簡單的Redis集群客戶端交互的語義。 通過這種方式,我們可以運(yùn)行一個(gè)示例,同時(shí)嘗試使節(jié)點(diǎn)發(fā)生故障或開始重新分片,以了解Redis集群在現(xiàn)實(shí)環(huán)境下的行為。只是觀察一個(gè)沒有寫入任何數(shù)據(jù)的集群是沒有幫助的。 本節(jié)說明了redis-rb-cluster的一些基本用法,其中顯示了兩個(gè)示例。 首先是以下內(nèi)容,它是redis-rb-cluster發(fā)行版中的example.rb文件:
該程序做了一件非常簡單的事情,它將foo
該程序看起來比較復(fù)雜,因?yàn)樗枰谄聊簧巷@示錯(cuò)誤而不是異常退出,因此,對(duì)集群執(zhí)行的每個(gè)操作都應(yīng)該由錯(cuò)誤處理包裝。 第14行是程序中的第一個(gè)有趣的行。它創(chuàng)建Redis集群對(duì)象,使用啟動(dòng)節(jié)點(diǎn)列表作為參數(shù),并允許該對(duì)象與不同節(jié)點(diǎn)建立的最大連接數(shù),最后是超時(shí)時(shí)間,對(duì)于給定的操作多少時(shí)間后被視為失敗。 啟動(dòng)節(jié)點(diǎn)不需要是集群的所有節(jié)點(diǎn)。但至少有一個(gè)節(jié)點(diǎn)是可達(dá)的。還要注意,只要能夠與第一個(gè)節(jié)點(diǎn)連接,redis-rb-cluster就會(huì)更新此啟動(dòng)節(jié)點(diǎn)列表。您應(yīng)該期望任何其他嚴(yán)格的客戶端都應(yīng)該采取這種行為。 現(xiàn)在我們已經(jīng)將Redis集群對(duì)象實(shí)例存儲(chǔ)在rc變量中,我們可以像使用普通的Redis對(duì)象實(shí)例一樣使用該對(duì)象了。 這恰好發(fā)生在第18至26行中:重新啟動(dòng)示例時(shí),我們不想以foo0重新開始,因此我們將計(jì)數(shù)器存儲(chǔ)在Redis本身內(nèi)。上面的代碼旨在讀取此計(jì)數(shù)器,或者如果不存在該計(jì)數(shù)器,則為其分配零值。 但是請(qǐng)注意這是一個(gè)while循環(huán),因?yàn)榧词辜宏P(guān)閉并返回錯(cuò)誤,我們也要一次又一次嘗試。普通的應(yīng)用程序不需要那么小心。 28和37之間開始主循環(huán),在該循環(huán)中設(shè)置key或顯示錯(cuò)誤。 注意循環(huán)結(jié)束時(shí)的sleep調(diào)用。在測試中,如果您想盡可能快地寫入集群,則可以刪除sleep(相對(duì)來說,這只是一個(gè)很繁忙的循環(huán)操作,它并沒有真正的并行,因此,在最好的條件下,您通常將獲得每秒10k個(gè)操作))。 通常,為了使示例程序更容易被人看懂,寫入速度會(huì)減慢。啟動(dòng)應(yīng)用程序?qū)a(chǎn)生以下輸出:
這不是一個(gè)非常有趣的程序,我們稍后將使用更好的程序,但是我們已經(jīng)可以看到程序運(yùn)行時(shí),在重新分片期間都發(fā)生了什么。 集群重新分片現(xiàn)在,我們準(zhǔn)備嘗試集群重新分片。 為此,請(qǐng)保持example.rb程序運(yùn)行,以便您查看對(duì)程序的運(yùn)行是否有影響。另外,您可能想注釋一下sleep調(diào)用,以便在重新分片期間發(fā)生一些更嚴(yán)重的寫入負(fù)載。重新分片基本上意味著將hash槽從一組節(jié)點(diǎn)移動(dòng)到另一組節(jié)點(diǎn),并且像集群創(chuàng)建一樣,它使用redis-cli程序完成。 要開始重新分片,只需鍵入:
您只需要指定一個(gè)節(jié)點(diǎn),redis-cli將自動(dòng)找到其他節(jié)點(diǎn)。 當(dāng)前redis-cli僅能在管理員支持下重新分片,您不能僅僅說將5%的插槽從該節(jié)點(diǎn)移到另一個(gè)節(jié)點(diǎn)(當(dāng)然這實(shí)現(xiàn)起來很簡單)。 因此,它會(huì)以一個(gè)問題開始。 首先是您想做多少重分片:
我們可以嘗試重新分派1000個(gè)hash槽,如果該示例仍在運(yùn)行且沒有sleep調(diào)用,則該hash槽應(yīng)已包含少量的key。 然后redis-cli需要知道重新分片的目標(biāo)是什么,也就是將接收hash槽的節(jié)點(diǎn)。 我將使用第一個(gè)主節(jié)點(diǎn),即127.0.0.1:7000,但是我需要指定實(shí)例的節(jié)點(diǎn)ID。redis-cli已將其打印在列表中,但是如果需要的話,我也可以使用以下命令找到節(jié)點(diǎn)的ID:
所以我的目標(biāo)節(jié)點(diǎn)應(yīng)該是是 97a3a64667477371c4479320d683e4c8db5858b1。 現(xiàn)在,它會(huì)問你要從哪些節(jié)點(diǎn)獲取這些key。我只輸入all,以便從所有其他主節(jié)點(diǎn)獲取一些hash槽。 最終確認(rèn)后,您會(huì)看到一條消息,表明redis-cli將要從一個(gè)節(jié)點(diǎn)移動(dòng)到另一個(gè)節(jié)點(diǎn),并且將從一側(cè)移動(dòng)到另一側(cè)的每個(gè)實(shí)際的key都會(huì)打印出來。 在重新分片過程中,您應(yīng)該能夠看到示例程序運(yùn)行不受影響。如果需要,您還可以在重新分片期間停止并重新啟動(dòng)它多次。重新分片結(jié)束時(shí),可以使用以下命令測試集群的運(yùn)行狀況:
所有插槽都會(huì)被覆蓋到,但是這次127.0.0.1:7000的主節(jié)點(diǎn)將具有更多的hash插槽 一個(gè)更有趣的示例應(yīng)用程序我們之前編寫的示例程序不怎么好。它以一種簡單的方式寫入集群,甚至無需檢查寫入的內(nèi)容是否正確。從我們的角度來看,接收寫操作的集群可以始終在每個(gè)操作里將名為foo的key寫到42這個(gè)hash槽里,而我們根本不會(huì)注意到。因此,在redis-rb-cluster代碼倉庫中,有一個(gè)更有趣的程序,稱為consistency-test.rb。它使用一組計(jì)數(shù)器,默認(rèn)為1000,并且發(fā)送INCR命令以增加計(jì)數(shù)器的值。但是,該應(yīng)用程序不僅可以寫數(shù)據(jù),還可以做兩件事:
這意味著該程序是一個(gè)簡單的一致性檢查程序,可以告訴您集群是否丟失了一些寫操作,或者它是否接受了我們未收到確認(rèn)的寫操作。在第一種情況下,我們將看到一個(gè)計(jì)數(shù)器的值小于我們之前記住的值,而在第二種情況下,該值將更大。 運(yùn)行一致性測試應(yīng)用程序每秒產(chǎn)生一行輸出:
該行顯示執(zhí)行的讀取和寫入的次數(shù),以及錯(cuò)誤的數(shù)目(由于系統(tǒng)不可用,因此由于錯(cuò)誤而無法接受查詢)。如果發(fā)現(xiàn)不一致,則將新行添加到輸出中。例如,如果我在程序運(yùn)行時(shí)手動(dòng)重置了計(jì)數(shù)器,就會(huì)發(fā)生這種情況:
當(dāng)我將計(jì)數(shù)器設(shè)置為0時(shí),實(shí)際值為114,因此程序會(huì)報(bào)告114的寫丟失了(集群無法記住的INCR命令)。該程序作為測試用例更加有趣,因此我們將使用它來測試Redis 集群故障轉(zhuǎn)移。 測試故障轉(zhuǎn)移注意:在此測試過程中,你應(yīng)打開一個(gè)tab標(biāo)簽頁并在上面運(yùn)行一致性測試應(yīng)用程序。 為了觸發(fā)故障轉(zhuǎn)移,我們可以做的最簡單的事情(也就是在分布式系統(tǒng)中可能發(fā)生的語義上最簡單的失?。┦鞘箚蝹€(gè)進(jìn)程崩潰,在我們的例子中是單個(gè)主機(jī)崩潰。 我們可以使用以下命令來識(shí)別主節(jié)點(diǎn)并使其崩潰:
好了,現(xiàn)在7000,7001,7002都是主節(jié)點(diǎn),我們把7002這臺(tái)機(jī)器用DEBUG SEGFAULT命令使其崩潰。
現(xiàn)在我們可以看看這個(gè)一致性測試的輸出的報(bào)告是什么。
如您所見,在故障轉(zhuǎn)移期間,系統(tǒng)無法接受578次讀取和577次寫入,但是在數(shù)據(jù)庫中并未創(chuàng)建任何不一致的數(shù)據(jù)。 這聽起來可能是個(gè)意外,因?yàn)樵诒窘坛痰牡谝徊糠种?,我們說過Redis集群在故障轉(zhuǎn)移期間會(huì)丟失寫操作,因?yàn)樗褂卯惒綇?fù)制。我們沒有說的是,這其實(shí)不太可能發(fā)生,因?yàn)镽edis會(huì)給客戶端發(fā)送回應(yīng),并且同樣的命令幾乎同時(shí)會(huì)復(fù)制到備份節(jié)點(diǎn),因此丟失數(shù)據(jù)的窗口很小。但是,很難觸發(fā)這一事實(shí)并不意味著它不可能,因此這不會(huì)改變Redis集群提供的一致性保證。 現(xiàn)在,我們可以檢查故障轉(zhuǎn)移之后的集群設(shè)置是什么(請(qǐng)注意,我重新啟動(dòng)了崩潰的實(shí)例,以便它作為備份節(jié)點(diǎn)重新加入集群):
現(xiàn)在,主節(jié)點(diǎn)在端口7000、7001和7005上運(yùn)行。以前是主節(jié)點(diǎn)(在端口7002上運(yùn)行的Redis實(shí)例)現(xiàn)在變成了7005的備份節(jié)點(diǎn)。 CLUSTER NODES命令的輸出可能看起來很復(fù)雜,但實(shí)際上非常簡單,由以下標(biāo)記組成:
手動(dòng)故障轉(zhuǎn)移有時(shí),強(qiáng)制進(jìn)行故障轉(zhuǎn)移而實(shí)際上不會(huì)對(duì)主節(jié)點(diǎn)引起任何問題是很有用的。例如,為了升級(jí)主節(jié)點(diǎn)之一的Redis進(jìn)程,最好對(duì)其進(jìn)行故障轉(zhuǎn)移,以將其轉(zhuǎn)變?yōu)閷?duì)可用性的影響最小的備份節(jié)點(diǎn)。 Redis集群使用CLUSTER FAILOVER 命令支持手動(dòng)故障轉(zhuǎn)移,該手動(dòng)故障轉(zhuǎn)移必須在要進(jìn)行故障轉(zhuǎn)移的主節(jié)點(diǎn)的備份節(jié)點(diǎn)之一中執(zhí)行。 與實(shí)際的主服務(wù)器故障導(dǎo)致的故障轉(zhuǎn)移相比,手動(dòng)故障轉(zhuǎn)移是不一樣的,但它更安全,因?yàn)樗鼈冇|發(fā)的方式避免了此過程中的數(shù)據(jù)丟失,只有在系統(tǒng)確定新的主節(jié)點(diǎn)已經(jīng)在運(yùn)行并且替代了舊的主節(jié)點(diǎn)的數(shù)據(jù)復(fù)制功能后,才能將客戶端從原來的主節(jié)點(diǎn)切換到新的主節(jié)點(diǎn)。 在執(zhí)行手動(dòng)故障轉(zhuǎn)移時(shí)在備份節(jié)點(diǎn)日志中可以看到:
基本上,連接到我們將要進(jìn)行故障轉(zhuǎn)移的主節(jié)點(diǎn)的客戶端都已停止。同時(shí),主節(jié)點(diǎn)將其復(fù)制偏移發(fā)送到備份節(jié)點(diǎn),備份節(jié)點(diǎn)會(huì)在它這邊等待偏移接收完畢。 當(dāng)復(fù)制偏移量完成時(shí),故障轉(zhuǎn)移開始,并且將向舊的主節(jié)點(diǎn)通知配置切換。 當(dāng)客戶端在舊的主節(jié)點(diǎn)上解鎖時(shí),它們將被重定向到新的主節(jié)點(diǎn)。 添加新節(jié)點(diǎn)添加新節(jié)點(diǎn)的基本過程是先添加一個(gè)空節(jié)點(diǎn),然后將一些數(shù)據(jù)移入該節(jié)點(diǎn)(如果它是新的主節(jié)點(diǎn)),或者告訴它設(shè)置為已知節(jié)點(diǎn)的副本(如果它是備份節(jié)點(diǎn))。從添加新的主節(jié)點(diǎn)開始,我們兩者都會(huì)展示。在這兩種情況下,要執(zhí)行的第一步都是添加一個(gè)空節(jié)點(diǎn)。這就像在端口7006中啟動(dòng)一個(gè)新節(jié)點(diǎn)(現(xiàn)有的6個(gè)節(jié)點(diǎn)已經(jīng)從7000到7005使用新節(jié)點(diǎn))一樣簡單,除了端口號(hào)之外,其他節(jié)點(diǎn)都使用相同的配置,因此您應(yīng)該按順序進(jìn)行操作以符合我們之前節(jié)點(diǎn)使用的設(shè)置:
此時(shí)這個(gè)服務(wù)應(yīng)該運(yùn)行起來了?,F(xiàn)在我們可以使用redis-cli來向已有的集群添加一個(gè)節(jié)點(diǎn)。
如您所見,我使用add-node命令將新節(jié)點(diǎn)的地址指定為第一個(gè)參數(shù),并將集群中隨機(jī)存在的節(jié)點(diǎn)的地址指定為第二個(gè)參數(shù)。實(shí)際上,redis-cli在這里對(duì)我們沒什么用,它只是向節(jié)點(diǎn)發(fā)送了CLUSTERMEET消息,這也可以手動(dòng)完成。不過redis-cli會(huì)在運(yùn)行之前檢查集群的狀態(tài),因此,即使您知道內(nèi)部結(jié)構(gòu)如何運(yùn)行,通過redis-cli執(zhí)行集群操作是仍然是一個(gè)好主意。 現(xiàn)在,我們可以連接到新節(jié)點(diǎn),以查看它是否確實(shí)加入了集群:
請(qǐng)注意,由于此節(jié)點(diǎn)已經(jīng)連接到集群,因此它已經(jīng)能夠正確重定向客戶端查詢,通常來說它已經(jīng)是集群的一部分了。 但是,與其他主節(jié)點(diǎn)相比,它有兩個(gè)特點(diǎn):
現(xiàn)在可以使用redis-cli的重新分片功能將hash槽分配給該節(jié)點(diǎn)。像上一節(jié)中已經(jīng)展示的那樣,這里我就不展示了,他們的操作沒有區(qū)別,只是將空節(jié)點(diǎn)作為目標(biāo)進(jìn)行重新分片。 添加一個(gè)節(jié)點(diǎn)作為副本(備份節(jié)點(diǎn))添加一個(gè)備份節(jié)點(diǎn)可以通過2種方式完成。最常用的是用 redis-cli, 不過要用
請(qǐng)注意,此處的命令行與我們用于添加新主節(jié)點(diǎn)的命令行完全相同,因此我們并未指定要向其添加副本的主節(jié)點(diǎn)。在這種情況下,redis-cli要做的就是將新節(jié)點(diǎn)添加給副本較少的主節(jié)點(diǎn)中的隨機(jī)主節(jié)點(diǎn)的副本。但是,您可以使用以下命令行指定想要與新副本一起使用的主節(jié)點(diǎn):
這樣我們便將新副本分配給特定的主節(jié)點(diǎn)。 將副本添加到特定主節(jié)點(diǎn)的一種更手動(dòng)的方法是將新節(jié)點(diǎn)添加為空的主節(jié)點(diǎn),然后使用CLUSTER REPLICATE命令將其轉(zhuǎn)換為副本。 如果將該節(jié)點(diǎn)添加為備份節(jié)點(diǎn),但您想將其作為其他主節(jié)點(diǎn)的副本進(jìn)行移動(dòng),也一樣適用。 例如,為了給節(jié)點(diǎn)127.0.0.1:7005添加副本,此節(jié)點(diǎn)當(dāng)前服務(wù)的hash槽正在11423-16383范圍內(nèi),節(jié)點(diǎn)ID為3c3a0c74aae0b56170ccb03a76b60cfe7dc1912e,我要做的就是連接到新節(jié)點(diǎn)(已經(jīng)作為空的主節(jié)點(diǎn)添加到集群)并在新節(jié)點(diǎn)上發(fā)送命令:
就是這樣。 現(xiàn)在,這組hash槽有了一個(gè)新副本,并且集群中的所有其他節(jié)點(diǎn)都已經(jīng)知道(幾秒鐘后需要更新其配置)。 我們可以使用以下命令進(jìn)行驗(yàn)證:
節(jié)點(diǎn)3c3a0c...現(xiàn)在有了2個(gè)備份節(jié)點(diǎn),運(yùn)行在7002端口(已存在)和7006端口(新加入的) 移除節(jié)點(diǎn)為了移除一個(gè)備份節(jié)點(diǎn),只需要在redis-cli上使用del-node 命令:
第一個(gè)參數(shù)只是集群中的一個(gè)隨機(jī)節(jié)點(diǎn),第二個(gè)參數(shù)是您要?jiǎng)h除的節(jié)點(diǎn)的ID。您也可以用相同的方法刪除主節(jié)點(diǎn),但是要?jiǎng)h除主節(jié)點(diǎn),它必須為空。如果主節(jié)點(diǎn)不為空,則需要先將數(shù)據(jù)重新分片到所有其他主節(jié)點(diǎn)。 刪除主節(jié)點(diǎn)的另一種方法是在其一個(gè)備份節(jié)點(diǎn)上對(duì)其執(zhí)行手動(dòng)故障轉(zhuǎn)移,并在該節(jié)點(diǎn)成為新主節(jié)點(diǎn)的備份節(jié)點(diǎn)之后刪除該節(jié)點(diǎn)。顯然,這在您想要減少集群中的主節(jié)點(diǎn)的實(shí)際數(shù)量時(shí)沒什么用,在這種情況下,需要重新分片。 副本遷移在Redis集群里里任何時(shí)間你都可以重新配置一個(gè)備份節(jié)點(diǎn)使其作為另一個(gè)主節(jié)點(diǎn)的從屬節(jié)點(diǎn),使用下列命令:
但是,有一種特殊情況,您希望副本在沒有系統(tǒng)管理員幫助的情況下自動(dòng)從一個(gè)主節(jié)點(diǎn)移動(dòng)到另一個(gè)主節(jié)點(diǎn)。副本的自動(dòng)重新配置稱為副本遷移,它能夠提高Redis集群的可靠性。 注意:您可以在Redis集群規(guī)范中閱讀副本遷移的詳細(xì)信息,這里我們僅提供一些一般的想法以及您應(yīng)該從中受益的信息。 在某些情況下,您可能想讓您的集群副本從一個(gè)主節(jié)點(diǎn)移動(dòng)到另一個(gè)主節(jié)點(diǎn)的原因是,Redis集群通常具有與給定主節(jié)點(diǎn)的副本數(shù)量相同的故障容忍性。 例如,如果一個(gè)主節(jié)點(diǎn)及其副本同時(shí)失敗,則每個(gè)主節(jié)點(diǎn)都有一個(gè)副本的集群將無法繼續(xù)工作,這僅僅是因?yàn)闆]有其他實(shí)例擁有與該主節(jié)點(diǎn)服務(wù)的相同的hash槽的副本。但是,盡管網(wǎng)絡(luò)斷裂可能會(huì)同時(shí)隔離多個(gè)節(jié)點(diǎn),但是許多其他類型的故障(例如單個(gè)節(jié)點(diǎn)本地的硬件或軟件故障)是非常值得注意的一類故障,這類故障不太可能同時(shí)發(fā)生,因此在每個(gè)主節(jié)點(diǎn)都有一個(gè)備份節(jié)點(diǎn)的集群中,如果該備份節(jié)點(diǎn)在凌晨4點(diǎn)被關(guān)閉,而主節(jié)點(diǎn)在凌晨6點(diǎn)被關(guān)閉。這仍然會(huì)導(dǎo)致集群無法運(yùn)行。 為了提高系統(tǒng)的可靠性,我們可以選擇向每個(gè)主節(jié)點(diǎn)添加副本,但這成本很高。副本遷移允許將更多備份節(jié)點(diǎn)添加到少數(shù)幾個(gè)主節(jié)點(diǎn)中。因此,您有10個(gè)節(jié)點(diǎn),每個(gè)節(jié)點(diǎn)有1個(gè)備份節(jié)點(diǎn),總共20個(gè)實(shí)例。但是,比如您增加了3個(gè)實(shí)例作為某些主節(jié)點(diǎn)的備份節(jié)點(diǎn),因此某些主節(jié)點(diǎn)將具有多個(gè)副本了。 使用副本遷移時(shí),如果一個(gè)主節(jié)點(diǎn)不包含備份節(jié)點(diǎn),則具有多個(gè)備份節(jié)點(diǎn)的主節(jié)點(diǎn)的副本將遷移到孤立的主節(jié)點(diǎn)。因此,當(dāng)您的備份節(jié)點(diǎn)在上述示例中的凌晨4點(diǎn)關(guān)閉之后,另一個(gè)備份節(jié)點(diǎn)將接替它;當(dāng)主節(jié)點(diǎn)在凌晨5點(diǎn)也發(fā)生故障時(shí),另一個(gè)備份節(jié)點(diǎn)將被選舉成為主節(jié)點(diǎn),以便集群可以繼續(xù)操作。 所以您應(yīng)該了解哪些有關(guān)副本遷移的知識(shí)?
|
|
|