鍍金池/ 教程/ 大數(shù)據(jù)/ Redis 集群(上)
Redis 數(shù)據(jù)淘汰機(jī)制
積分排行榜
小剖 Memcache
Redis 數(shù)據(jù)結(jié)構(gòu) intset
分布式鎖
從哪里開始讀起,怎么讀
Redis 數(shù)據(jù)結(jié)構(gòu) dict
不在浮沙筑高臺
Redis 集群(上)
Redis 監(jiān)視器
源碼閱讀工具
Redis 日志和斷言
內(nèi)存數(shù)據(jù)管理
Redis 數(shù)據(jù)結(jié)構(gòu)綜述
源碼日志
Web 服務(wù)器存儲 session
消息中間件
Redis 與 Lua 腳本
什么樣的源代碼適合閱讀
Redis 數(shù)據(jù)結(jié)構(gòu) sds
Memcached slab 分配策略
訂閱發(fā)布機(jī)制
Redis 是如何提供服務(wù)的
Redis 事務(wù)機(jī)制
Redis 集群(下)
主從復(fù)制
Redis 應(yīng)用
RDB 持久化策略
Redis 數(shù)據(jù)遷移
Redis 事件驅(qū)動詳解
初探 Redis
Redis 與 Memcache
AOF 持久化策略
Redis 數(shù)據(jù)結(jié)構(gòu) redisOb
作者簡介
Redis 數(shù)據(jù)結(jié)構(gòu) ziplist
Redis 數(shù)據(jù)結(jié)構(gòu) skiplist
Redis 哨兵機(jī)制

Redis 集群(上)

前奏

集群的概念早在 Redis 3.0 之前討論了,3.0 才在源碼中出現(xiàn)。Redis 集群要考慮的問題:

  1. 節(jié)點之間怎么據(jù)的同步,如何做到數(shù)據(jù)一致性。一主一備的模式,可以用 Redis 內(nèi)部實現(xiàn)的主從備份實現(xiàn)數(shù)據(jù)同步。但節(jié)點不斷增多,存在多個 master 的時候,同步的難度會越大。
  2. 如何做到負(fù)載均衡?請求量大的時候,如何將請求盡量均分到各個服務(wù)器節(jié)點,負(fù)載均衡算法做的不好會導(dǎo)致雪崩。
  3. 如何做到平滑拓展?當(dāng)業(yè)務(wù)量增加的時候,能否通過簡單的配置即讓新的 Redis 節(jié)點變?yōu)榭捎谩?/li>
  4. 可用性如何?當(dāng)某些節(jié)點鼓掌,能否快速恢復(fù)服務(wù)器集群的工作能力。
  5. ……

一個穩(wěn)健的后臺系統(tǒng)需要太多的考慮。

也談一致性哈希算法(consistent hashing)

背景

通常,業(yè)務(wù)量較大的時候,考慮到性能的問題(索引速度慢和訪問量過大),不會把所有的數(shù)據(jù)存放在一個 Redis 服務(wù)器上。這里需要將一堆的鍵值均分存儲到多個 Redis 服務(wù)器,可以通過:

target = hash(key)\%N,其中 target 為目標(biāo)節(jié)點,key 為鍵,N 為 Redis 節(jié)點的個數(shù)哈希取余的方式會將不同的 key 分發(fā)到不同的服務(wù)器上。

但考慮如下場景:

  1. 業(yè)務(wù)量突然增加,現(xiàn)有服務(wù)器不夠用。增加服務(wù)器節(jié)點后,依然通過上面的計算方式:hash(key)%(N+1) 做數(shù)據(jù)分片和分發(fā),但之前的 key 會被分發(fā)到與之前不同的服務(wù)器上,導(dǎo)致大量的數(shù)據(jù)失效,需要重新寫入(set)Redis 服務(wù)器。
  2. 其中的一個服務(wù)器掛了。如果不做及時的修復(fù),大量被分發(fā)到此服務(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á)到均衡。

虛擬節(jié)點簡介

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é)點

虛擬節(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),也可以在中間件上實現(xiàn)(如 proxy)。在客戶端實現(xiàn)中,當(dāng)客戶端初始化的時候,需要初始化一張預(yù)備的 Redis 節(jié)點的映射表:hash(key)=> . 這有一個缺點,假設(shè)有多個客戶端,當(dāng)映射表發(fā)生變化的時候,多個客戶端需要同時拉取新的映射表。

另一個種是中間件(proxy)的實現(xiàn)方法,即在客戶端和 Redis 節(jié)點之間加多一個代理,代理經(jīng)過哈希計算后將對應(yīng)某個 key 的請求分發(fā)到對應(yīng)的節(jié)點,一致性哈希算法就在中間件里面實現(xiàn)。可以發(fā)現(xiàn),twemproxy 就是這么做的。

twemproxy - Redis 集群管理方案

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 也開始支持集群特性了,再也不用靠著外援過日子了。基本的思想是,集群里的每個 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ì)則。