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

集群(下)

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

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

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

手動故障轉(zhuǎn)移很特別,和真正因為主服務(wù)器失效而產(chǎn)生的故障轉(zhuǎn)移要更安全,因為采取了避免過程中數(shù)據(jù)丟失的方式,僅當(dāng)系統(tǒng)確認(rèn)新的主服務(wù)器處理完了舊的主服務(wù)器的復(fù)制流時,客戶端才從原主服務(wù)器切換到新主服務(wù)器。

下面是當(dāng)你手動故障轉(zhuǎn)移時你從從服務(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ù)器的客戶端停止了。與此同時,主服務(wù)器發(fā)送復(fù)制偏移量給從服務(wù)器,等待到達(dá)這個偏移量。當(dāng)復(fù)制偏移量到達(dá)以后,故障轉(zhuǎn)移就開始了,舊的主服務(wù)器被通知切換配置。當(dāng)客戶端在舊主服務(wù)器上解除阻塞時,就被重定向到新的主服務(wù)器。

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

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

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

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

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

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

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

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

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

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

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

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

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  

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

  • 因為沒有分配哈希槽所以沒有數(shù)據(jù)。
  • 因為這個主服務(wù)器沒有分配哈希槽,所以當(dāng)有從服務(wù)器要變成主服務(wù)器時不能參與選舉過程。

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

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

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

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

注意,這里的命令行完全像我們在添加一個新主服務(wù)器時使用的一樣,所以我們沒有指定要給哪個主服務(wù)器添加副本。這種情況下,redis-trib 會添加一個新節(jié)點作為一個具有較少副本的隨機(jī)的主服務(wù)器的副本。

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

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

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

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

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

redis 127.0.0.1:7006> cluster replicate 3c3a0c74aae0b56170ccb03a76b60cfe7dc1912e  

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

$ 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  

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

移除節(jié)點(Removing a node)

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

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

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

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

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

副本遷移(Replicas migration)

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

CLUSTER REPLICATE <master-node-id>  

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

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

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

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

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

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

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

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

升級節(jié)點(Upgrading nodes in a Redis Cluster)

升級從服務(wù)器節(jié)點很簡單,因為你只需要停止節(jié)點然后用已更新的 Redis 版本重啟。如果有客戶端使用從服務(wù)器節(jié)點分離讀請求,它們應(yīng)該能夠在某個節(jié)點不可用時重新連接另一個從服務(wù)器。

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

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

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

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

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

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

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

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

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

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

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

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

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