集群的概念早在 Redis 3.0 之前討論了,3.0 才在源碼中出現(xiàn)。Redis 集群要考慮的問題:
一個穩(wěn)健的后臺系統(tǒng)需要太多的考慮。
通常,業(yè)務(wù)量較大的時候,考慮到性能的問題(索引速度慢和訪問量過大),不會把所有的數(shù)據(jù)存放在一個 Redis 服務(wù)器上。這里需要將一堆的鍵值均分存儲到多個 Redis 服務(wù)器,可以通過:
target = hash(key)\%N
,其中 target 為目標(biāo)節(jié)點,key 為鍵,N 為 Redis 節(jié)點的個數(shù)哈希取余的方式會將不同的 key 分發(fā)到不同的服務(wù)器上。
但考慮如下場景:
hash(key)%(N+1)
做數(shù)據(jù)分片和分發(fā),但之前的 key 會被分發(fā)到與之前不同的服務(wù)器上,導(dǎo)致大量的數(shù)據(jù)失效,需要重新寫入(set)Redis 服務(wù)器。這也是兩個問題。
http://wiki.jikexueyuan.com/project/redis/images/h.png" alt="" />
設(shè)定一個圓環(huán)上 0 23?2-1 的點,每個點對應(yīng)一個緩存區(qū),每個鍵值對存儲的位置也經(jīng)哈希計算后對應(yīng)到環(huán)上節(jié)點。但現(xiàn)實中不可能有如此多的節(jié)點,所以倘若鍵值對經(jīng)哈希計算后對應(yīng)的位置沒有節(jié)點,那么順時針找一個節(jié)點存儲它。
http://wiki.jikexueyuan.com/project/redis/images/h1.png" alt="" />
考慮增加服務(wù)器節(jié)點的情況,該節(jié)點順時針方向的數(shù)據(jù)仍然被存儲到順時針方向的節(jié)點上,但它逆時針方向的數(shù)據(jù)被存儲到它自己。這時候只有部分?jǐn)?shù)據(jù)會失效,被映射到新的緩存區(qū)。
http://wiki.jikexueyuan.com/project/redis/images/h2.png" alt="" />
考慮節(jié)點減少的情況。該缺失節(jié)點順時針方向上的數(shù)據(jù)仍然被存儲到其順時針方向上的節(jié)點,設(shè)為 beta,其逆時針方向上的數(shù)據(jù)會被存儲到 beta 上。同樣,只有有部分?jǐn)?shù)據(jù)失效,被重新映射到新的服務(wù)器節(jié)點。
http://wiki.jikexueyuan.com/project/redis/images/h3.png" alt="" />
這種情況比較麻煩,上面圖中 gamma 節(jié)點失效后,會有大量數(shù)據(jù)映射到 alpha 節(jié)點,最怕 alpha 扛不住,接下去 beta 也扛不住,這就是多米諾骨牌效應(yīng);)。這里涉及到數(shù)據(jù)平衡性和負(fù)載均衡的話題。數(shù)據(jù)平衡性是說,數(shù)據(jù)盡可能均分到每個節(jié)點上去,存儲達(dá)到均衡。
http://wiki.jikexueyuan.com/project/redis/images/h4.png" alt="" />
將多個虛擬節(jié)點對應(yīng)到一個真實的節(jié)點,存儲可以達(dá)到更均衡的效果。之前的映射方案為:
key -> node
中間多了一個層虛擬節(jié)點后,多了一層映射關(guān)系:
key -> <virtual node> -> node
虛擬節(jié)點的設(shè)計有什么好處?假設(shè)有四個節(jié)點如下:
http://wiki.jikexueyuan.com/project/redis/images/h5.png" alt="" />
節(jié)點 3 突然宕機(jī),這時候原本在節(jié)點 3 的數(shù)據(jù),會被定向到節(jié)點 4。在三個節(jié)點中節(jié)點 4 的請求量是最大的。這就導(dǎo)致節(jié)點與節(jié)點之間請求量是不均衡的。
http://wiki.jikexueyuan.com/project/redis/images/h6.png" alt="" />
為了達(dá)到節(jié)點與節(jié)點之間請求訪問的均衡,嘗試將原有節(jié)點 3 的數(shù)據(jù)平均定向到到節(jié)點 1,2,4. 如此達(dá)到負(fù)載均衡的效果,如下:
http://wiki.jikexueyuan.com/project/redis/images/h7.png" alt="" />
總之,一致性哈希算法是希望在增刪節(jié)點的時候,讓盡可能多的緩存數(shù)據(jù)不失效。
一致性哈希算法,既可以在客戶端實現(xiàn),也可以在中間件上實現(xiàn)(如 proxy)。在客戶端實現(xiàn)中,當(dāng)客戶端初始化的時候,需要初始化一張預(yù)備的 Redis 節(jié)點的映射表:hash(key)=>
另一個種是中間件(proxy)的實現(xiàn)方法,即在客戶端和 Redis 節(jié)點之間加多一個代理,代理經(jīng)過哈希計算后將對應(yīng)某個 key 的請求分發(fā)到對應(yīng)的節(jié)點,一致性哈希算法就在中間件里面實現(xiàn)。可以發(fā)現(xiàn),twemproxy 就是這么做的。
twemproxy 是 twitter 開源的一個輕量級的后端代理,兼容 redis/memcache 協(xié)議,可用以管理 redis/memcache 集群。
http://wiki.jikexueyuan.com/project/redis/images/h8.png" alt="" />
twemproxy 內(nèi)部有實現(xiàn)一致性哈希算法,對于客戶端而言,twemproxy 相當(dāng)于是緩存數(shù)據(jù)庫的入口,它無需知道后端的部署是怎樣的。twemproxy 會檢測與每個節(jié)點的連接是否健康,出現(xiàn)異常的節(jié)點會被剔除;待一段時間后,twemproxy 會再次嘗試連接被剔除的節(jié)點。
通常,一個 Redis 節(jié)點池可以分由多個 twemproxy 管理,少數(shù) twemproxy 負(fù)責(zé)寫,多數(shù)負(fù)責(zé)讀。twemproxy 可以實時獲取節(jié)點池內(nèi)的所有 Redis 節(jié)點的狀態(tài),但其對故障修復(fù)的支持還有待提高。解決的方法是可以借助 redis sentinel 來實現(xiàn)自動的主從切換,當(dāng)主機(jī) down 掉后,sentinel 會自動將從機(jī)配置為主機(jī)。而 twemproxy 可以定時向 redis sentinel 拉取信息,從而替換出現(xiàn)異常的節(jié)點。
http://wiki.jikexueyuan.com/project/redis/images/h9.png" alt="" />
twemproxy 的更多細(xì)節(jié),這里不再做深入的討論。
最新版本的 Redis 也開始支持集群特性了,再也不用靠著外援過日子了。基本的思想是,集群里的每個 Redis 都只存儲一定的鍵值對,這個“一定”可以通過默認(rèn)或自定義的哈希函數(shù)來決定,當(dāng)一個 Redis 收到請求后,會首先查看此鍵值對是否該由自己來處理,是則繼續(xù)往下執(zhí)行;否則會產(chǎn)生一個類似于 http 3XX 的重定向,要求客戶端去請求集群中的另一個 Redis。
Redis 每一個實例都會通過遵守一定的協(xié)議來維護(hù)這個集群的可用性,穩(wěn)定性。有興趣可前往官網(wǎng)了解 Redis 集群的實現(xiàn)細(xì)則。