鍍金池/ 教程/ 大數(shù)據(jù)/ 集群(下)
使用 Redis 實(shí)現(xiàn) Twitter(上)
集群(下)
使用 Redis 實(shí)現(xiàn) Twitter(下)
使用 Redis 作為 LRU 緩存
高可用(上)
高可用客戶端指引
集群(中)
高可用(下)
持久化
Redis 介紹
集中插入
集群(上)
從入門到精通(上)
從入門到精通(下)
從入門到精通(中)
分片
數(shù)據(jù)類型初探
復(fù)制

集群(下)

手動(dòng)故障轉(zhuǎn)移(Manual failover)

有時(shí)候在主服務(wù)器事實(shí)上沒有任何故障的情況下強(qiáng)制一次故障轉(zhuǎn)移是很有用的。例如,為了升級(jí)主服務(wù)器節(jié)點(diǎn)中的一個(gè)進(jìn)程,可以對(duì)其進(jìn)行故障轉(zhuǎn)移使其變?yōu)橐粋€(gè)從服務(wù)器,這樣最小化了對(duì)可用性的影響。

Redis 集群支持使用 CLUSTER FAILOVER 命令來手動(dòng)故障轉(zhuǎn)移,必須在你想進(jìn)行故障轉(zhuǎn)移的主服務(wù)的其中一個(gè)從服務(wù)器上執(zhí)行。

手動(dòng)故障轉(zhuǎn)移很特別,和真正因?yàn)橹鞣?wù)器失效而產(chǎn)生的故障轉(zhuǎn)移要更安全,因?yàn)椴扇×吮苊膺^程中數(shù)據(jù)丟失的方式,僅當(dāng)系統(tǒng)確認(rèn)新的主服務(wù)器處理完了舊的主服務(wù)器的復(fù)制流時(shí),客戶端才從原主服務(wù)器切換到新主服務(wù)器。

下面是當(dāng)你手動(dòng)故障轉(zhuǎn)移時(shí)你從從服務(wù)器日志中看到的內(nèi)容:

# Manual failover user request accepted.  
# Received replication offset for paused master manual failover: 347540  
# All master replication stream processed, manual failover can start.  
# Start of election delayed for 0 milliseconds (rank #0, offset 347540).  
# Starting a failover election for epoch 7545.  
# Failover election won: I'm the new master.  

基本上,連接到我們正在故障轉(zhuǎn)移的主服務(wù)器的客戶端停止了。與此同時(shí),主服務(wù)器發(fā)送復(fù)制偏移量給從服務(wù)器,等待到達(dá)這個(gè)偏移量。當(dāng)復(fù)制偏移量到達(dá)以后,故障轉(zhuǎn)移就開始了,舊的主服務(wù)器被通知切換配置。當(dāng)客戶端在舊主服務(wù)器上解除阻塞時(shí),就被重定向到新的主服務(wù)器。

添加新節(jié)點(diǎn)(Adding a new node)

添加一個(gè)新節(jié)點(diǎn)的過程基本上就是,添加一個(gè)空節(jié)點(diǎn),然后,如果是作為主節(jié)點(diǎn)則移動(dòng)一些數(shù)據(jù)進(jìn)去,如果是從節(jié)點(diǎn)則其作為某個(gè)節(jié)點(diǎn)的副本。

兩種情況我們都會(huì)討論,先從添加一個(gè)新的主服務(wù)器實(shí)例開始。

兩種情況下,第一步要完成的都是添加一個(gè)空節(jié)點(diǎn)。

我們使用與其他節(jié)點(diǎn)相同的配置(端口號(hào)除外)在 7006 端口(我們已存在的 6 個(gè)節(jié)點(diǎn)已經(jīng)使用了從 7000 到 7005 的端口)上開啟一個(gè)新的節(jié)點(diǎn),那么為了與我們之前的節(jié)點(diǎn)布局一致,你得這么做:

  • 在你的終端程序中開啟一個(gè)新的標(biāo)簽窗口。
  • 進(jìn)入 cluster-test 目錄。
  • 創(chuàng)建一個(gè)名為 7006 的目錄。
  • 在里面創(chuàng)建一個(gè) redis.conf 的文件,類似于其它節(jié)點(diǎn)使用的文件,但是使用 7006 作為端口號(hào)。
  • 最后使用../redis-server ./redis.conf 啟動(dòng)服務(wù)器。

此時(shí)服務(wù)器已經(jīng)在運(yùn)行中了。

現(xiàn)在我們可以像通常一樣使用 redis-trib 來添加節(jié)點(diǎn)到已存在的集群中。

./redis-trib.rb add-node 127.0.0.1:7006 127.0.0.1:7000  

你可以看到,我使用了 addnode 命令,指定新的節(jié)點(diǎn)地址為第一個(gè)參數(shù),集群中一個(gè)隨機(jī)存在的節(jié)點(diǎn)的地址作為第二個(gè)參數(shù)。

實(shí)際上 redis-trib 在這里對(duì)我們只有很少的幫助,只是發(fā)送了一個(gè) CLUSTER MEET 消息到節(jié)點(diǎn),這些也可以手動(dòng)完成。但是 redis-trib 也在操作之前檢查了集群的狀態(tài),所以即便你知道內(nèi)部是如何工作的,一直通過 redis-trib 來執(zhí)行集群操作也是一個(gè)不錯(cuò)的主意。

現(xiàn)在,我們可以連接到這個(gè)新的節(jié)點(diǎn),看看它是否真的加入到了集群中:

redis 127.0.0.1:7006> cluster nodes  
3e3a6cb0d9a9a87168e266b0a0b24026c0aae3f0 127.0.0.1:7001 master - 0 1385543178575 0 connected 5960-10921  
3fc783611028b1707fd65345e763befb36454d73 127.0.0.1:7004 slave 3e3a6cb0d9a9a87168e266b0a0b24026c0aae3f0 0 1385543179583 0 connected  
f093c80dde814da99c5cf72a7dd01590792b783b :0 myself,master - 0 0 0 connected  
2938205e12de373867bf38f1ca29d31d0ddb3e46 127.0.0.1:7002 slave 3c3a0c74aae0b56170ccb03a76b60cfe7dc1912e 0 1385543178072 3 connected  
a211e242fc6b22a9427fed61285e85892fa04e08 127.0.0.1:7003 slave 97a3a64667477371c4479320d683e4c8db5858b1 0 1385543178575 0 connected  
97a3a64667477371c4479320d683e4c8db5858b1 127.0.0.1:7000 master - 0 1385543179080 0 connected 0-5959 10922-11422  
3c3a0c74aae0b56170ccb03a76b60cfe7dc1912e 127.0.0.1:7005 master - 0 1385543177568 3 connected 11423-16383  

注意,因?yàn)檫@個(gè)節(jié)點(diǎn)已經(jīng)連接到集群了,所以也已經(jīng)可以正確地重定向客戶端查詢,簡(jiǎn)而言之,這個(gè)節(jié)點(diǎn)已經(jīng)是集群的一部分了。但是它比其他主節(jié)點(diǎn)有兩個(gè)特殊之處:

  • 因?yàn)闆]有分配哈希槽所以沒有數(shù)據(jù)。
  • 因?yàn)檫@個(gè)主服務(wù)器沒有分配哈希槽,所以當(dāng)有從服務(wù)器要變成主服務(wù)器時(shí)不能參與選舉過程。

現(xiàn)在可以使用 redis-trib 的重新分片特性來給這個(gè)節(jié)點(diǎn)賦予哈希槽了?;旧蠜]有必現(xiàn)展示這個(gè)了,因?yàn)槲覀円呀?jīng)在之前的小節(jié)中展示過了,沒有什么不同,只是以空節(jié)點(diǎn)為目標(biāo)的一次重分片。

添加副本節(jié)點(diǎn)(Adding a new node as a replica)

添加一個(gè)新副本可以有兩種方式。顯而易見的一種方式是再次使用 redis-trib,但是要使用—slave 選項(xiàng),像這樣:

./redis-trib.rb add-node --slave 127.0.0.1:7006 127.0.0.1:7000  

注意,這里的命令行完全像我們?cè)谔砑右粋€(gè)新主服務(wù)器時(shí)使用的一樣,所以我們沒有指定要給哪個(gè)主服務(wù)器添加副本。這種情況下,redis-trib 會(huì)添加一個(gè)新節(jié)點(diǎn)作為一個(gè)具有較少副本的隨機(jī)的主服務(wù)器的副本。

但是,你可以使用下面的命令行精確地指定你想要的主服務(wù)器作為副本的目標(biāo):

./redis-trib.rb add-node --slave --master-id 3c3a0c74aae0b56170ccb03a76b60cfe7dc1912e 127.0.0.1:7006 127.0.0.1:7000  

這樣我們就把一個(gè)新的副本賦予了一個(gè)指定的主服務(wù)器。

一種更手工的給指定主服務(wù)器添加副本的方式,是添加一個(gè)新節(jié)點(diǎn)作為一個(gè)空主服務(wù)器,然后使用 CLUSTER REPLICATE 命令將其變?yōu)楦北?。如果?jié)點(diǎn)被作為從服務(wù)器添加,但是你想移動(dòng)它為另一個(gè)不同的主服務(wù)器的副本,這也是可行的。

例如,為了給節(jié)點(diǎn) 127.0.0.1:7005 添加一個(gè)副本,這個(gè)節(jié)點(diǎn)當(dāng)前服務(wù) 11432-16383 范圍內(nèi)的哈希槽,其節(jié)點(diǎn) ID 為 3c3a0c74aae0b56170ccb03a76b60cfe7dc1912e,所有我們需要去做的,就是連接這個(gè)新的節(jié)點(diǎn)(已經(jīng)作為空主服務(wù)器被添加)然后發(fā)送命令:

redis 127.0.0.1:7006> cluster replicate 3c3a0c74aae0b56170ccb03a76b60cfe7dc1912e  

就是這樣?,F(xiàn)在我們有了這組哈希槽的一個(gè)新副本了,集群中的其它節(jié)點(diǎn)也已經(jīng)知道了(需要幾秒鐘來更新配置)。我們可以用下面的命令來核實(shí)一下:

$ redis-cli -p 7000 cluster nodes | grep slave | grep 3c3a0c74aae0b56170ccb03a76b60cfe7dc1912e  
f093c80dde814da99c5cf72a7dd01590792b783b 127.0.0.1:7006 slave 3c3a0c74aae0b56170ccb03a76b60cfe7dc1912e 0 1385543617702 3 connected  
2938205e12de373867bf38f1ca29d31d0ddb3e46 127.0.0.1:7002 slave 3c3a0c74aae0b56170ccb03a76b60cfe7dc1912e 0 1385543617198 3 connect  

這個(gè) 3c3a0c…的節(jié)點(diǎn)現(xiàn)在有兩個(gè)從服務(wù)器了,分別運(yùn)行于 7002(已存在的)和 7006(新的)端口。

移除節(jié)點(diǎn)(Removing a node)

要移除一個(gè)從服務(wù)器節(jié)點(diǎn),只要使用 redis-trib 的 del-node 命令就可以:

./redis-trib del-node 127.0.0.1:7000 <node-id>  

第一個(gè)參數(shù)只是集群中的一個(gè)隨機(jī)節(jié)點(diǎn),第二個(gè)參數(shù)是你想移除的節(jié)點(diǎn)的 ID。

你也可以用同樣的方式移除一個(gè)主服務(wù)器節(jié)點(diǎn),但是,為了移除一個(gè)主服務(wù)器節(jié)點(diǎn),它必須是空的。如果主服務(wù)器不是空的,你需要先將其數(shù)據(jù)重分片到其他的主服務(wù)器節(jié)點(diǎn)。

另一種移除主服務(wù)器節(jié)點(diǎn)的方式,就是在其從服務(wù)器上執(zhí)行一次手工故障轉(zhuǎn)移,當(dāng)它變?yōu)榱诵碌闹鞣?wù)器的從服務(wù)器以后將其移除。顯然,當(dāng)你需要真的減少你的集群中的主服務(wù)器的數(shù)量時(shí)這個(gè)沒有什么幫助,如果那樣的話,就需要重新分片了。

副本遷移(Replicas migration)

在 Redis 集群中,可以使用下面的命令在任何時(shí)候重新配置一個(gè)從服務(wù)器復(fù)制一個(gè)不同的主服務(wù)器:

CLUSTER REPLICATE <master-node-id>  

但是有一種特殊的場(chǎng)景,你想讓副本集自動(dòng)地從一個(gè)主服務(wù)器移動(dòng)到另一個(gè)主服務(wù)器,而不需要系統(tǒng)管理員的幫助。自動(dòng)重新配置副本集被稱為副本集遷移,這可以改善 Redis 集群的可靠性。

注意:你可以在 Redis 集群規(guī)范中閱讀到副本集遷移的細(xì)節(jié),這里我們只提供一般性的信息,以及為了從中受益你該做什么。

為什么你想讓你的集群副本在某些特定條件下從一個(gè)主服務(wù)器移動(dòng)到另一個(gè)的原因,是通常情況下 Redis 集群對(duì)失敗的抵御能力和連接到指定從服務(wù)器的副本數(shù)量成正相關(guān)。

例如,每個(gè)主服務(wù)器只有單個(gè)副本組成的集群在主服務(wù)器及其副本同時(shí)失效時(shí)就不能夠繼續(xù)運(yùn)轉(zhuǎn),因?yàn)闆]有其他的實(shí)例擁有這臺(tái)主服務(wù)器服務(wù)的哈希槽的副本。但是,網(wǎng)絡(luò)斷裂可能會(huì)在同一時(shí)間隔絕若干節(jié)點(diǎn),其他類型的故障,例如單個(gè)節(jié)點(diǎn)的硬件或者軟件錯(cuò)誤,是值得注意的一類故障,很可能會(huì)同時(shí)發(fā)生,所以在每個(gè)主服務(wù)器擁有一個(gè)從服務(wù)器的集群中,有可能從服務(wù)器在下午 4 點(diǎn)被干掉,而主服務(wù)器在下午 6 點(diǎn)被干掉。這仍然會(huì)導(dǎo)致集群不再能運(yùn)轉(zhuǎn)。

為了改進(jìn)系統(tǒng)的可靠性,我們有一些增加額外副本集到每個(gè)主服務(wù)器的選項(xiàng),但是這代價(jià)昂貴。副本遷移允許添加更多的從服務(wù)器到少許的主服務(wù)器。所以你可以有 10 個(gè)主服務(wù)器,每個(gè)有一個(gè)從服務(wù)器,總共 20 個(gè)實(shí)例。但是你為某些主服務(wù)器添加例如 3 個(gè)以上的實(shí)例,那么有些主服務(wù)器會(huì)有多余一個(gè)的從服務(wù)器。

有了副本集遷移會(huì)發(fā)生什么?如果一個(gè)主服務(wù)器沒有從服務(wù)器,一個(gè)來自于擁有多個(gè)從服務(wù)器的主服務(wù)器上的從服務(wù)器會(huì)遷移到這個(gè)孤獨(dú)的主服務(wù)器上。所以像上面我們舉的例子中,在你的從服務(wù)器下午 4 點(diǎn)下線以后,另一個(gè)從服務(wù)器會(huì)接替它的位置,當(dāng)主服務(wù)器在下午 5 點(diǎn)也失效的時(shí)候,仍然有一個(gè)從服務(wù)器可以被選舉,這樣集群就可以繼續(xù)運(yùn)轉(zhuǎn)了。

那么,簡(jiǎn)而言之,你應(yīng)該了解副本集遷移的什么呢?

  • 集群會(huì)嘗試從在某一個(gè)指定時(shí)刻擁有最多數(shù)量副本集的主服務(wù)上遷移一個(gè)副本。
  • 為了從副本遷移中受益,你需要在集群中添加多一些的副本到單個(gè)主服務(wù)器,無論是什么主服務(wù)器。
  • 有一個(gè)稱為 replica-migration-barrier 的控制副本遷移特性的配置參數(shù)。你可以在 Redis 群提供的示例 redis.conf 文件中讀到更多信息。

升級(jí)節(jié)點(diǎn)(Upgrading nodes in a Redis Cluster)

升級(jí)從服務(wù)器節(jié)點(diǎn)很簡(jiǎn)單,因?yàn)槟阒恍枰V构?jié)點(diǎn)然后用已更新的 Redis 版本重啟。如果有客戶端使用從服務(wù)器節(jié)點(diǎn)分離讀請(qǐng)求,它們應(yīng)該能夠在某個(gè)節(jié)點(diǎn)不可用時(shí)重新連接另一個(gè)從服務(wù)器。

升級(jí)主服務(wù)器要稍微復(fù)雜一些,建議的步驟是:

  1. 使用 CLUSTER FAILOVER 來觸發(fā)一次手工故障轉(zhuǎn)移主服務(wù)器(請(qǐng)看本文檔的手工故障轉(zhuǎn)移小節(jié))。
  2. 等待主服務(wù)器變?yōu)閺姆?wù)器。
  3. 像升級(jí)從服務(wù)器那樣升級(jí)這個(gè)節(jié)點(diǎn)。
  4. 如果你想讓你剛剛升級(jí)的節(jié)點(diǎn)成為主服務(wù)器,觸發(fā)一次新的手工故障轉(zhuǎn)移,讓升級(jí)的節(jié)點(diǎn)重新變回主服務(wù)器。

你可以按照這些步驟來一個(gè)節(jié)點(diǎn)一個(gè)節(jié)點(diǎn)的升級(jí),直到全部節(jié)點(diǎn)升級(jí)完畢。

遷移到 Redis 集群(Migrating to Redis Cluster)

想遷移到 Redis 集群的用戶可能只有一個(gè)單一的主服務(wù)器,或者已經(jīng)使用了已存在的分片布局,通過使用某種內(nèi)部算法,或者他們的客戶端庫實(shí)現(xiàn)的分片算法,或者 Redis 代理,鍵被分拆到 N 個(gè)節(jié)點(diǎn)上。

這兩種情況下遷移到 Redis 集群都很簡(jiǎn)單,但是最重要的細(xì)節(jié)是,如果程序使用了多鍵操作,怎么辦。有三種不同的情況:

  1. 沒有使用多鍵操作,或者事務(wù),或者涉及多個(gè)鍵的 Lua 腳本。鍵被獨(dú)立地訪問(即使通過事務(wù)或者 Lua 腳本組合針對(duì)同樣的鍵的多個(gè)命令一起來訪問)。
  2. 使用了多鍵操作,或者事務(wù),或者涉及多個(gè)鍵的 Lua 腳本,但是鍵都有相同的哈希標(biāo)簽(hash tag),也就是說這些一起使用的鍵都碰巧有相同的{…}子串。例如,下面的多鍵操作是在相同的哈希標(biāo)簽上下文中定義的:SUNION {user:1000}.foo {user:1000}.bar。
  3. 使用了多鍵操作,或者事務(wù),或者涉及多個(gè)鍵的 Lua 腳本,但是鍵的名字沒有一個(gè)顯式的或者相同的哈希標(biāo)簽。

Redis 不處理第三種情況:應(yīng)用程序需要被修改為不能使用多鍵操作,或者只能在相同的哈希標(biāo)簽上下文中使用。

前兩種情況覆蓋到了,所以我們會(huì)聚焦在這兩種情況,它們會(huì)用相同的方式來處理,所以本文不會(huì)去區(qū)別對(duì)待。

假設(shè)你的已存在數(shù)據(jù)集已經(jīng)被拆分到了 N 個(gè)主服務(wù)器上,如果你沒有已存在的分片的話 N=1,你需要下面的步驟來遷移你的數(shù)據(jù)集到 Redis 集群:

  1. 停止你的客戶端。當(dāng)前沒有自動(dòng)在線遷移(live-migration)到 Redis 集群的可能。你也許可以通過精心策劃一次在你的程序或環(huán)境上下文中的在線遷移來辦到。
  2. 使用 BGREWRITEOF 命令為所有你的 N 個(gè)主服務(wù)器生成一個(gè)追加文件,然后等待 AOF 文件完全生成。
  3. 按照 aof-1 到 af-N 保存你的 AOF 文件到某處。此時(shí)愿意的話你可以停掉你的舊實(shí)例(這很有用,因?yàn)樵诜翘摂M化的部署中,你常常需要重用這些計(jì)算機(jī))。
  4. 創(chuàng)建一個(gè)由 N 個(gè)主服務(wù)器和 0 個(gè)從服務(wù)器組成的 Redis 集群。你可以稍后添加從服務(wù)器。確保所有你的節(jié)點(diǎn)都是使用追加文件來持久化。
  5. 停止所有的集群節(jié)點(diǎn),用你已存在的追加文件替換他們的朱家文件,aof-1 替換第一節(jié)點(diǎn),aof-2 替換第二個(gè)節(jié)點(diǎn),一直到 aof-N。
  6. 使用新的 AOF 文件來重啟你的 Redis 集群。它們會(huì)抱怨按照配置有些鍵不應(yīng)該出現(xiàn)。
  7. 使用 redis-trib fix 命令來修正集群,這樣鍵就會(huì)根據(jù)每個(gè)節(jié)點(diǎn)的哈希槽被遷移了。
  8. 最后使用 redis-trib check 來確保集群是正常的。
  9. 重啟被修改為支持 Redis 集群的客戶端。

還有一個(gè)方式從外部實(shí)例導(dǎo)入數(shù)據(jù)到 Redis 集群,就是使用 redis-trib import 命令。

這個(gè)命令移動(dòng)一個(gè)運(yùn)行實(shí)例(同時(shí)刪除源實(shí)例上的鍵)上的所有鍵到一個(gè)指定已存在的 Redis 集群。但是,注意如果你使用 Redis 2.8 實(shí)例作為來源實(shí)例,操作可能很慢,因?yàn)?2.8 沒有實(shí)現(xiàn)遷移連接緩存(migrate connection caching),所以在執(zhí)行這個(gè)操作之前,你可能得重啟你的 Redis 3.x 版本的源實(shí)例。