Redis的集群模式是在Redis3.0模式以后所實行的高可用模式。雖然大部分公司還都在用3.0以下的模式,但是隨著發(fā)展我們會慢慢的接觸到3.0以上的形式。在這里我們先簡單的介紹下集群的模式,方便我們后期來用。
Redis的集群介紹
Redis的集群是一個提供多個Redis節(jié)點(diǎn)之間數(shù)據(jù)共享的程序集。但是Redis集群并不支持處理多個keys的命令,因為這需要在不同的節(jié)點(diǎn)移動數(shù)據(jù),在高負(fù)載的情況下可能導(dǎo)致不可預(yù)料的錯誤。Redis集群通過分區(qū)來提供一定程度的可用性,這樣情況的優(yōu)勢在于,
- 能自動的分割數(shù)據(jù)到不同的節(jié)點(diǎn)上。
- 整個集群的部分節(jié)點(diǎn)失敗或者不可達(dá)的情況下能繼續(xù)處理命令。
Redis集群的數(shù)據(jù)分片
Redis的集群沒有使用一致性的hash,而是引入了哈希槽的概念。哈希槽就是在Redis中使用CRC16檢驗后對16384進(jìn)行取模 來決定放置在哪個槽。Redis中一共有16384個槽 ,所以在取模的時候我們會對16384進(jìn)行操作。集群中有N個機(jī)器,那么16384節(jié)點(diǎn)會平分這些槽。這樣就很容易方便我們方便我們操作機(jī)器的增加和刪除,并且不影響我們集群的狀態(tài)。
Redis集群的主從復(fù)制模型
前面的文章中我們介紹的Redis中的復(fù)制功能。在集群中,我們也是用了相關(guān)的操作,為了防止在部分節(jié)點(diǎn)失敗的或者無法通信的情況下集群仍然可用。所以我們在集群中使用了主從復(fù)制模型,每個節(jié)點(diǎn)都會有N-1個復(fù)制品。當(dāng)主節(jié)點(diǎn)失敗的時候,從節(jié)點(diǎn)會直接代替舊的主節(jié)點(diǎn)成為新的主節(jié)點(diǎn)。
Redis的一致性保證
Redis 并不能保證數(shù)據(jù)的強(qiáng)一致性,這在操作中容易出現(xiàn)丟失的寫操作。為什么會出現(xiàn)這種情況呢,是因為Redis的集群采取的異步復(fù)制的方式。寫操作的過程中
- 客戶端向主節(jié)點(diǎn)寫入一條命令。
- 主節(jié)點(diǎn)master向客戶端回復(fù)一條命令狀態(tài)
- 主節(jié)點(diǎn)將寫操作復(fù)制給他的從節(jié)點(diǎn)
主節(jié)點(diǎn)對命令的復(fù)制工作發(fā)生在返回命令回復(fù)之后,因為如果每次請求命令都需要等待復(fù)制操作完成的情況下,那么主節(jié)點(diǎn)處理命令的請求速度回極大地降低,所以我們必須在性能和一致性操作做出權(quán)衡。 另外出現(xiàn)數(shù)據(jù)丟失的情況是 集群出現(xiàn)了網(wǎng)絡(luò)分區(qū),并且一個客戶端與至少一個主節(jié)點(diǎn)在內(nèi)的少數(shù)實例被孤立。
錯誤的分區(qū)那么相當(dāng)于主節(jié)點(diǎn)就會出現(xiàn)問題,雖然在這期間仍然能像某個出現(xiàn)問題的主節(jié)點(diǎn)上繼續(xù)寫入數(shù)據(jù),但是在大的分區(qū)那里從節(jié)點(diǎn)會被選舉為新的主節(jié)點(diǎn),這樣在選舉期間造成的數(shù)據(jù)寫入到舊的數(shù)據(jù)master上,就會數(shù)據(jù)丟失了。所以我們在配置集群的時候 節(jié)點(diǎn)的超時時間配置(node timeout)就是十分重要了。
集群的重新分片
在上面我們也說過,我們在集群中可以方便的增加節(jié)點(diǎn)和刪除節(jié)點(diǎn),但是有個問題就是我們需要進(jìn)行重新進(jìn)行分片。在Redis中我們的分片操作并不影響我們系統(tǒng)的運(yùn)行,我們可以使用
./redis-trib.rb reshard 127.0.0.1:7000 數(shù)據(jù)進(jìn)行分片
我們知道如果集群已經(jīng)穩(wěn)定的運(yùn)行了一段時間,那么在我們想移動的槽里面一般都會存在數(shù)據(jù)的,所以我們在移動的時候也得知道移動的哈希槽數(shù)量之外 ,還的知道分片的目標(biāo)地址。
$ redis-cli -p 7000 cluster nodes | grep myself
87a3a3236747727124449322d633e1c3db5421b1 :0 myself,master - 0 0 0 connected 0-5460
其中目標(biāo)節(jié)點(diǎn)是87a3a3236747727124449322d633e1c3db5421b1,現(xiàn)在需要指定從哪些節(jié)點(diǎn)來移動keys到目標(biāo)節(jié)點(diǎn) 我輸入的是all ,這樣就會從其他每個master上取一些哈希槽。
最后確認(rèn)后你將會看到每個redis-trib移動的槽的信息,每個key的移動的信息也會打印出來 在重新分片的過程中,你的例子程序是不會受到影響的,你可以停止或者重新啟動多次。
Redis集群的目標(biāo)
Redis的集群在3.0版本以后增加了,為什么會專門在Redis中實現(xiàn)集群的方式呢,在這里我們看到的是Redis為了提高高可用的性能所做的努力。
- 集群目標(biāo)是在1000個節(jié)點(diǎn)的時候仍然能表現(xiàn)的很好,并且有很好的擴(kuò)展性。
- 沒有合并的操作,這樣在Redi的數(shù)據(jù)模型上大數(shù)據(jù)值也能表現(xiàn)的很好。
- 寫入安全,嘗試讓大多數(shù)節(jié)點(diǎn)相連的客戶端寫入操作都能保證的安全的寫入,雖然現(xiàn)在還會有小部分寫入丟失。
- 可用性,在絕大多數(shù)節(jié)點(diǎn)是可達(dá)的情況下,對于不可達(dá)的主節(jié)點(diǎn)在有一個從節(jié)點(diǎn)可達(dá)的情況下,Redis集群仍能進(jìn)行分區(qū)操作。
Redis 集群中客戶端與服務(wù)端
Redis中節(jié)點(diǎn)負(fù)責(zé)存儲數(shù)據(jù)、記錄集群的狀態(tài)。集群節(jié)點(diǎn)同樣能自動發(fā)現(xiàn)其他節(jié)點(diǎn),檢測出沒有正常工作的節(jié)點(diǎn),并且在節(jié)點(diǎn)中 選出主節(jié)點(diǎn),那么為了這些操作,Redis之間的節(jié)點(diǎn)是通過TCP鏈接和一個二進(jìn)制鏈接簡歷通訊,每一個節(jié)點(diǎn)都活通過集群鏈接與集群上的其他節(jié)點(diǎn)鏈接起來,并且通過使用一個gossip協(xié)議來傳播集群的信息。這樣發(fā)現(xiàn)新的節(jié)點(diǎn)、通過網(wǎng)絡(luò)ping, 特定情況發(fā)生時發(fā)送集群消息。 由于集群節(jié)點(diǎn)不能代理 請求,所以客戶端在接收到重定向錯誤的時會將命令重定向其他節(jié)點(diǎn)??蛻舳耸强梢宰杂傻南窦褐腥我夤?jié)點(diǎn)發(fā)送請求。也可以在需要的時候重定向到其他節(jié)點(diǎn)來保證命令的執(zhí)行效率。
安全寫入
Redis集群之間節(jié)點(diǎn)采用了異步的冗余備份。所以偶爾在分區(qū)的時候會存在時間段容易丟失數(shù)據(jù)。但是就如我們上面所說的兩種情況會導(dǎo)致數(shù)據(jù)的丟失??偨Y(jié)情況如下:
- 寫入操作能到達(dá)一個節(jié)點(diǎn)上,當(dāng)回復(fù)節(jié)點(diǎn)時主節(jié)點(diǎn)宕機(jī)了,然而數(shù)據(jù)還沒有同步到從節(jié)點(diǎn),那么該寫入就已經(jīng)丟失,從節(jié)點(diǎn)被提為主節(jié)點(diǎn)。
- 因為分區(qū)使一個主節(jié)點(diǎn)不可達(dá)。
- 故障轉(zhuǎn)移導(dǎo)致主節(jié)點(diǎn)的從節(jié)點(diǎn)升為主節(jié)點(diǎn)
- 過一段時間后主節(jié)點(diǎn)再次可達(dá)
- 一個沒有更新路由表的客戶端或許會在集群把主節(jié)點(diǎn)變成從節(jié)點(diǎn)。導(dǎo)致寫入失敗。
所以我們在這是node_timeout的時候需要慎重,容易導(dǎo)致數(shù)據(jù)丟失的問題
為什么有的單機(jī)操作使用的命令在集群 中不可使用
Redis集群在設(shè)計的時候是避免在多個節(jié)點(diǎn)中存在同個鍵值對的沖突版本。Redis中的數(shù)據(jù)模型不允許這么做,有時候值特別大,那么我們在列表或者排序號的集合中就會有大量的元素,傳輸和合并就會有性能瓶頸。
