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