Redis集群方案
時(shí)間:2023-04-18 21:00:01 | 來源:網(wǎng)站運(yùn)營
時(shí)間:2023-04-18 21:00:01 來源:網(wǎng)站運(yùn)營
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ò)展,這需要由多臺主機(jī)協(xié)同提供服務(wù),即分布式多個(gè)Redis實(shí)例協(xié)同運(yùn)行。
其次,目前硬件資源成本降低,多核CPU,幾十G內(nèi)存的主機(jī)很普遍,對于主進(jìn)程是單線程工作的Redis,只運(yùn)行一個(gè)實(shí)例就顯得有些浪費(fèi)。同時(shí),管理一個(gè)巨大內(nèi)存不如管理相對較小的內(nèi)存高效。因此,實(shí)際使用中,通常一臺機(jī)器上同時(shí)跑多個(gè)Redis實(shí)例。
方案1.Redis官方集群方案 Redis ClusterRedis Cluster是一種服務(wù)器Sharding技術(shù),3.0版本開始正式提供。
Redis Cluster中,Sharding采用slot(槽)的概念,一共分成16384個(gè)槽,這有點(diǎn)類似pre sharding思路。對于每個(gè)進(jìn)入Redis的鍵值對,根據(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都對應(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è)槽對應(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ù)對外提供服務(wù)。這非常類似前篇文章提到的Redis Sharding場景下服務(wù)器節(jié)點(diǎn)通過Sentinel監(jiān)控架構(gòu)成主從結(jié)構(gòu),只是Redis Cluster本身提供了故障轉(zhuǎn)移容錯(cuò)的能力。
Redis Cluster的新節(jié)點(diǎn)識別能力、故障判斷及故障轉(zhuǎn)移能力是通過集群中的每個(gè)node都在和其它nodes進(jìn)行通信,這被稱為集群總線(cluster bus)。它們使用特殊的端口號,即對外服務(wù)端口號加10000。例如如果某個(gè)node的端口號是6379,那么它與其它nodes通信的端口號是16379。nodes之間的通信采用特殊的二進(jìn)制協(xié)議。
對客戶端來說,整個(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 3正式推出了官方集群技術(shù),解決了多Redis實(shí)例協(xié)同服務(wù)問題。Redis Cluster可以說是服務(wù)端Sharding分片技術(shù)的體現(xiàn),即將鍵值按照一定算法合理分配到各個(gè)實(shí)例分片上,同時(shí)各個(gè)實(shí)例節(jié)點(diǎn)協(xié)調(diào)溝通,共同對外承擔(dān)一致服務(wù)。
多Redis實(shí)例服務(wù),比單Redis實(shí)例要復(fù)雜的多,這涉及到定位、協(xié)同、容錯(cuò)、擴(kuò)容等技術(shù)難題。這里,我們介紹一種輕量級的客戶端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ù)。Sharding架構(gòu)如圖:
慶幸的是,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ì)對每個(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í)很重要。
Redis Sharding采用客戶端Sharding方式,服務(wù)端Redis還是一個(gè)個(gè)相對獨(dú)立的Redis實(shí)例節(jié)點(diǎn),沒有做任何變動(dòng)。同時(shí),我們也不需要增加額外的中間處理組件,這是一種非常輕量、靈活的Redis多實(shí)例集群方法。
當(dāng)然,Redis Sharding這種輕量靈活方式必然在集群其它能力方面做出妥協(xié)。比如擴(kuò)容,當(dāng)想要增加Redis節(jié)點(diǎn)時(shí),盡管采用一致性哈希,畢竟還是會(huì)有key匹配不到而丟失,這時(shí)需要鍵值遷移。
作為輕量級客戶端sharding,處理Redis鍵值遷移是不現(xiàn)實(shí)的,這就要求應(yīng)用層面允許Redis中數(shù)據(jù)丟失或從后端數(shù)據(jù)庫重新加載數(shù)據(jù)。但有些時(shí)候,擊穿緩存層,直接訪問數(shù)據(jù)庫層,會(huì)對系統(tǒng)訪問造成很大壓力。有沒有其它手段改善這種情況?
Redis作者給出了一個(gè)比較討巧的辦法--presharding,即預(yù)先根據(jù)系統(tǒng)規(guī)模盡量部署好多個(gè)Redis實(shí)例,這些實(shí)例占用系統(tǒng)資源很小,一臺物理機(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)測。
3.利用代理中間件實(shí)現(xiàn)大規(guī)模Redis集群上面分別介紹了多Redis服務(wù)器集群的兩種方式,它們是基于客戶端sharding的Redis Sharding和基于服務(wù)端sharding的Redis Cluster。
客戶端sharding技術(shù)其優(yōu)勢在于服務(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)勢在于服務(wù)端Redis集群拓?fù)浣Y(jié)構(gòu)變化時(shí),客戶端不需要感知,客戶端像使用單Redis服務(wù)器一樣使用Redis集群,運(yùn)維管理也比較方便。
不過Redis Cluster正式版推出時(shí)間不長,系統(tǒng)穩(wěn)定性、性能等都需要時(shí)間檢驗(yàn),尤其在大規(guī)模使用場合。
能不能結(jié)合二者優(yōu)勢?即能使服務(wù)端各實(shí)例彼此獨(dú)立,支持線性可伸縮,同時(shí)sharding又能集中處理,方便統(tǒng)一管理?本篇介紹的Redis代理中間件twemproxy就是這樣一種利用中間件做sharding的技術(shù)。
twemproxy處于客戶端和服務(wù)器的中間,將客戶端發(fā)來的請求,進(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)址是:
https://github.com/twitter/twemproxytwemproxy后端不僅支持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í)測結(jié)果大約降低了20%左右。