Redis集群方案
通常,為了提高網(wǎng)站響應(yīng)速度牵素,總是把熱點(diǎn)數(shù)據(jù)保存在內(nèi)存中而不是直接從后端數(shù)據(jù)庫中讀取粮揉。Redis是一個(gè)很好的Cache工具巡李。大型網(wǎng)站應(yīng)用,熱點(diǎn)數(shù)據(jù)量往往巨大扶认,幾十G上百G是很正常的事兒侨拦,在這種情況下,如何正確架構(gòu)Redis呢蝠引?
首先阳谍,無論我們是使用自己的物理主機(jī),還是使用云服務(wù)主機(jī)螃概,內(nèi)存資源往往是有限制的,scale up不是一個(gè)好辦法鸽疾,我們需要scale out橫向可伸縮擴(kuò)展吊洼,這需要由多臺(tái)主機(jī)協(xié)同提供服務(wù),即分布式多個(gè)Redis實(shí)例協(xié)同運(yùn)行制肮。
其次冒窍,目前硬件資源成本降低,多核CPU豺鼻,幾十G內(nèi)存的主機(jī)很普遍综液,對(duì)于主進(jìn)程是單線程工作的Redis,只運(yùn)行一個(gè)實(shí)例就顯得有些浪費(fèi)儒飒。同時(shí)谬莹,管理一個(gè)巨大內(nèi)存不如管理相對(duì)較小的內(nèi)存高效。因此桩了,實(shí)際使用中附帽,通常一臺(tái)機(jī)器上同時(shí)跑多個(gè)Redis實(shí)例。
1.Redis官方集群方案 Redis Cluster
Redis Cluster是一種服務(wù)器Sharding技術(shù)井誉,3.0版本開始正式提供蕉扮。
Redis Cluster中,Sharding采用slot(槽)的概念颗圣,一共分成16384個(gè)槽喳钟,這有點(diǎn)兒類pre sharding思路。對(duì)于每個(gè)進(jìn)入Redis的鍵值對(duì)在岂,根據(jù)key進(jìn)行散列奔则,分配到這16384個(gè)slot中的某一個(gè)中。使用的hash算法也比較簡單洁段,就是CRC16后16384取模应狱。
Redis集群中的每個(gè)node(節(jié)點(diǎn))負(fù)責(zé)分?jǐn)傔@16384個(gè)slot中的一部分,也就是說祠丝,每個(gè)slot都對(duì)應(yīng)一個(gè)node負(fù)責(zé)處理疾呻。當(dāng)動(dòng)態(tài)添加或減少node節(jié)點(diǎn)時(shí)除嘹,需要將16384個(gè)槽做個(gè)再分配,槽中的鍵值也要遷移岸蜗。當(dāng)然尉咕,這一過程,在目前實(shí)現(xiàn)中璃岳,還處于半自動(dòng)狀態(tài)年缎,需要人工介入。
Redis集群铃慷,要保證16384個(gè)槽對(duì)應(yīng)的node都正常工作单芜,如果某個(gè)node發(fā)生故障,那它負(fù)責(zé)的slots也就失效犁柜,整個(gè)集群將不能工作洲鸠。
為了增加集群的可訪問性,官方推薦的方案是將node配置成主從結(jié)構(gòu)馋缅,即一個(gè)master主節(jié)點(diǎn)扒腕,掛n個(gè)slave從節(jié)點(diǎn)。這時(shí)萤悴,如果主節(jié)點(diǎn)失效瘾腰,Redis Cluster會(huì)根據(jù)選舉算法從slave節(jié)點(diǎn)中選擇一個(gè)上升為主節(jié)點(diǎn),整個(gè)集群繼續(xù)對(duì)外提供服務(wù)覆履。這非常類似前篇文章提到的Redis Sharding場(chǎng)景下服務(wù)器節(jié)點(diǎn)通過Sentinel監(jiān)控架構(gòu)成主從結(jié)構(gòu)蹋盆,只是Redis Cluster本身提供了故障轉(zhuǎn)移容錯(cuò)的能力。
Redis Cluster的新節(jié)點(diǎn)識(shí)別能力内狗、故障判斷及故障轉(zhuǎn)移能力是通過集群中的每個(gè)node都在和其它nodes進(jìn)行通信怪嫌,這被稱為集群總線(cluster bus)。它們使用特殊的端口號(hào)柳沙,即對(duì)外服務(wù)端口號(hào)加10000岩灭。例如如果某個(gè)node的端口號(hào)是6379,那么它與其它nodes通信的端口號(hào)是16379赂鲤。nodes之間的通信采用特殊的二進(jìn)制協(xié)議噪径。
對(duì)客戶端來說,整個(gè)cluster被看做是一個(gè)整體数初,客戶端可以連接任意一個(gè)node進(jìn)行操作找爱,就像操作單一Redis實(shí)例一樣,當(dāng)客戶端操作的key沒有分配到該node上時(shí)泡孩,Redis會(huì)返回轉(zhuǎn)向指令车摄,指向正確的node,這有點(diǎn)兒像瀏覽器頁面的302 redirect跳轉(zhuǎn)。
Redis Cluster是Redis 3.0以后才正式推出吮播,時(shí)間較晚变屁,目前能證明在大規(guī)模生產(chǎn)環(huán)境下成功的案例還不是很多,需要時(shí)間檢驗(yàn)意狠。
2.Redis Sharding集群
多Redis實(shí)例服務(wù)粟关,比單Redis實(shí)例要復(fù)雜的多,這涉及到定位环戈、協(xié)同闷板、容錯(cuò)、擴(kuò)容等技術(shù)難題院塞。這里遮晚,我們介紹一種輕量級(jí)的客戶端Redis Sharding技術(shù)。
Redis Sharding可以說是Redis Cluster出來之前迫悠,業(yè)界普遍使用的多Redis實(shí)例集群方法鹏漆。其主要思想是采用哈希算法將Redis數(shù)據(jù)的key進(jìn)行散列,通過hash函數(shù)创泄,特定的key會(huì)映射到特定的Redis節(jié)點(diǎn)上。這樣括蝠,客戶端就知道該向哪個(gè)Redis節(jié)點(diǎn)操作數(shù)據(jù)鞠抑。
慶幸的是,java redis客戶端驅(qū)動(dòng)jedis忌警,已支持Redis Sharding功能搁拙,即ShardedJedis以及結(jié)合緩存池的ShardedJedisPool。
Jedis的Redis Sharding實(shí)現(xiàn)具有如下特點(diǎn):
采用一致性哈希算法(consistent hashing)法绵,將key和節(jié)點(diǎn)name同時(shí)hashing箕速,然后進(jìn)行映射匹配,采用的算法是MURMUR_HASH朋譬。采用一致性哈希而不是采用簡單類似哈希求模映射的主要原因是當(dāng)增加或減少節(jié)點(diǎn)時(shí)盐茎,不會(huì)產(chǎn)生由于重新匹配造成的rehashing。一致性哈希只影響相鄰節(jié)點(diǎn)key分配徙赢,影響量小字柠。
為了避免一致性哈希只影響相鄰節(jié)點(diǎn)造成節(jié)點(diǎn)分配壓力,ShardedJedis會(huì)對(duì)每個(gè)Redis節(jié)點(diǎn)根據(jù)名字(沒有狡赐,Jedis會(huì)賦予缺省名字)會(huì)虛擬化出160個(gè)虛擬節(jié)點(diǎn)進(jìn)行散列窑业。根據(jù)權(quán)重weight,也可虛擬化出160倍數(shù)的虛擬節(jié)點(diǎn)枕屉。用虛擬節(jié)點(diǎn)做映射匹配常柄,可以在增加或減少Redis節(jié)點(diǎn)時(shí),key在各Redis節(jié)點(diǎn)移動(dòng)再分配更均勻,而不是只有相鄰節(jié)點(diǎn)受影響西潘。
ShardedJedis支持keyTagPattern模式卷玉,即抽取key的一部分keyTag做sharding,這樣通過合理命名key秸架,可以將一組相關(guān)聯(lián)的key放入同一個(gè)Redis節(jié)點(diǎn)揍庄,這在避免跨節(jié)點(diǎn)訪問相關(guān)數(shù)據(jù)時(shí)很重要。
Sharding
- redis服務(wù)器節(jié)點(diǎn)劃分:將每臺(tái)服務(wù)器節(jié)點(diǎn)采用hash算法劃分為160個(gè)虛擬節(jié)點(diǎn)(可以配置劃分權(quán)重)
- 將劃分虛擬節(jié)點(diǎn)采用TreeMap存儲(chǔ)
- 對(duì)每個(gè)redis服務(wù)器的物理連接采用LinkedHashMap存儲(chǔ)
- 對(duì)Key or KeyTag 采用同樣的hash算法东抹,然后從TreeMap獲取大于等于鍵hash值得節(jié)點(diǎn)蚂子,取最鄰近節(jié)點(diǎn)存儲(chǔ);當(dāng)key的hash值大于虛擬節(jié)點(diǎn)hash值得最大值時(shí)缭黔,存入第一個(gè)虛擬節(jié)點(diǎn)
- sharded采用的hash算法:MD5 和 MurmurHash兩種食茎;默認(rèn)采用64位的MurmurHash算法
擴(kuò)容問題
當(dāng)想要增加Redis節(jié)點(diǎn)時(shí),盡管采用一致性哈希馏谨,畢竟還是會(huì)有key匹配不到而丟失别渔,這時(shí)需要鍵值遷移。
作為輕量級(jí)客戶端sharding惧互,處理Redis鍵值遷移是不現(xiàn)實(shí)的哎媚,這就要求應(yīng)用層面允許Redis中數(shù)據(jù)丟失或從后端數(shù)據(jù)庫重新加載數(shù)據(jù)。
但有些時(shí)候喊儡,擊穿緩存層拨与,直接訪問數(shù)據(jù)庫層,會(huì)對(duì)系統(tǒng)訪問造成很大壓力艾猜。有沒有其它手段改善這種情況买喧?
preshardin方案:預(yù)先分配好足夠的分片,擴(kuò)容時(shí)只是將屬于某一分片的原Redis實(shí)例替換成新的容量更大的Redis實(shí)例匆赃。
即預(yù)先根據(jù)系統(tǒng)規(guī)模盡量部署好多個(gè)Redis實(shí)例淤毛,這些實(shí)例占用系統(tǒng)資源很小,一臺(tái)物理機(jī)可部署多個(gè)算柳,讓他們都參與sharding低淡,當(dāng)需要擴(kuò)容時(shí),選中一個(gè)實(shí)例作為主節(jié)點(diǎn)埠居,新加入的Redis節(jié)點(diǎn)作為從節(jié)點(diǎn)進(jìn)行數(shù)據(jù)復(fù)制查牌。數(shù)據(jù)同步后,修改sharding配置滥壕,讓指向原實(shí)例的Shard指向新機(jī)器上擴(kuò)容后的Redis節(jié)點(diǎn)纸颜,同時(shí)調(diào)整新Redis節(jié)點(diǎn)為主節(jié)點(diǎn),原實(shí)例可不再使用绎橘。
presharding是預(yù)先分配好足夠的分片胁孙,擴(kuò)容時(shí)只是將屬于某一分片的原Redis實(shí)例替換成新的容量更大的Redis實(shí)例唠倦。參與sharding的分片沒有改變,所以也就不存在key值從一個(gè)區(qū)轉(zhuǎn)移到另一個(gè)分片區(qū)的現(xiàn)象涮较,只是將屬于同分片區(qū)的鍵值從原Redis實(shí)例同步到新Redis實(shí)例稠鼻。
并不是只有增刪Redis節(jié)點(diǎn)引起鍵值丟失問題,更大的障礙來自Redis節(jié)點(diǎn)突然宕機(jī)狂票。在《Redis持久化》一文中已提到候齿,為不影響Redis性能,盡量不開啟AOF和RDB文件保存功能闺属,可架構(gòu)Redis主備模式慌盯,主Redis宕機(jī),數(shù)據(jù)不會(huì)丟失掂器,備Redis留有備份亚皂。
這樣,我們的架構(gòu)模式變成一個(gè)Redis節(jié)點(diǎn)切片包含一個(gè)主Redis和一個(gè)備Redis国瓮。在主Redis宕機(jī)時(shí)灭必,備Redis接管過來,上升為主Redis乃摹,繼續(xù)提供服務(wù)禁漓。主備共同組成一個(gè)Redis節(jié)點(diǎn),通過自動(dòng)故障轉(zhuǎn)移孵睬,保證了節(jié)點(diǎn)的高可用性璃饱。則Sharding架構(gòu)演變成:
Redis Sentinel提供了主備模式下Redis監(jiān)控、故障轉(zhuǎn)移功能達(dá)到系統(tǒng)的高可用性肪康。
高訪問量下,即使采用Sharding分片撩穿,一個(gè)單獨(dú)節(jié)點(diǎn)還是承擔(dān)了很大的訪問壓力磷支,這時(shí)我們還需要進(jìn)一步分解。通常情況下食寡,應(yīng)用訪問Redis讀操作量和寫操作量差異很大雾狈,讀常常是寫的數(shù)倍,這時(shí)我們可以將讀寫分離抵皱,而且讀提供更多的實(shí)例數(shù)善榛。
可以利用主從模式實(shí)現(xiàn)讀寫分離,主負(fù)責(zé)寫呻畸,從負(fù)責(zé)只讀移盆,同時(shí)一主掛多個(gè)從。在Sentinel監(jiān)控下伤为,還可以保障節(jié)點(diǎn)故障的自動(dòng)監(jiān)測(cè)咒循。
3.利用代理中間件實(shí)現(xiàn)大規(guī)模Redis集群
上面分別介紹了多Redis服務(wù)器集群的兩種方式,它們是基于客戶端sharding的Redis Sharding和基于服務(wù)端sharding的Redis Cluster。
客戶端sharding技術(shù)其優(yōu)勢(shì)在于服務(wù)端的Redis實(shí)例彼此獨(dú)立叙甸,相互無關(guān)聯(lián)颖医,每個(gè)Redis實(shí)例像單服務(wù)器一樣運(yùn)行,非常容易線性擴(kuò)展裆蒸,系統(tǒng)的靈活性很強(qiáng)熔萧。其不足之處在于:
由于sharding處理放到客戶端,規(guī)模進(jìn)步擴(kuò)大時(shí)給運(yùn)維帶來挑戰(zhàn)僚祷。
服務(wù)端Redis實(shí)例群拓?fù)浣Y(jié)構(gòu)有變化時(shí)佛致,每個(gè)客戶端都需要更新調(diào)整。
連接不能共享久妆,當(dāng)應(yīng)用規(guī)模增大時(shí)晌杰,資源浪費(fèi)制約優(yōu)化。
服務(wù)端sharding的Redis Cluster其優(yōu)勢(shì)在于服務(wù)端Redis集群拓?fù)浣Y(jié)構(gòu)變化時(shí)筷弦,客戶端不需要感知肋演,客戶端像使用單Redis服務(wù)器一樣使用Redis集群,運(yùn)維管理也比較方便烂琴。
不過Redis Cluster正式版推出時(shí)間不長爹殊,系統(tǒng)穩(wěn)定性、性能等都需要時(shí)間檢驗(yàn)奸绷,尤其在大規(guī)模使用場(chǎng)合梗夸。
能不能結(jié)合二者優(yōu)勢(shì)?即能使服務(wù)端各實(shí)例彼此獨(dú)立号醉,支持線性可伸縮反症,同時(shí)sharding又能集中處理,方便統(tǒng)一管理畔派?
本篇介紹的Redis代理中間件twemproxy就是這樣一種利用中間件做sharding的技術(shù)铅碍。類似的還有codis。
twemproxy處于客戶端和服務(wù)器的中間线椰,將客戶端發(fā)來的請(qǐng)求胞谈,進(jìn)行一定的處理后(如sharding),再轉(zhuǎn)發(fā)給后端真正的Redis服務(wù)器憨愉。也就是說烦绳,客戶端不直接訪問Redis服務(wù)器,而是通過twemproxy代理中間件間接訪問配紫。
參照Redis Sharding架構(gòu)径密,增加代理中間件的Redis集群架構(gòu)如下:
twemproxy中間件的內(nèi)部處理是無狀態(tài)的,它本身可以很輕松地集群笨蚁,這樣可避免單點(diǎn)壓力或故障睹晒。
twemproxy又叫nutcracker趟庄,起源于twitter系統(tǒng)中redis/memcached集群開發(fā)實(shí)踐,運(yùn)行效果良好伪很,后代碼奉獻(xiàn)給開源社區(qū)戚啥。其輕量高效,采用C語言開發(fā)锉试,工程網(wǎng)址是:GitHub - twitter/twemproxy: A fast, light-weight proxy for memcached and redis**
twemproxy后端不僅支持redis猫十,同時(shí)也支持memcached,這是twitter系統(tǒng)具體環(huán)境造成的呆盖。
由于使用了中間件拖云,twemproxy可以通過共享與后端系統(tǒng)的連接,降低客戶端直接連接后端服務(wù)器的連接數(shù)量应又。同時(shí)宙项,它也提供sharding功能,支持后端服務(wù)器集群水平擴(kuò)展株扛。統(tǒng)一運(yùn)維管理也帶來了方便尤筐。
當(dāng)然,也是由于使用了中間件代理洞就,相比客戶端直連服務(wù)器方式盆繁,性能上會(huì)有所損耗,實(shí)測(cè)結(jié)果大約降低了20%左右旬蟋。