亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Redis中切片集群詳解

 更新時間:2025年01月15日 11:07:45   作者:Recently?祝祝  
切片集群Redis中,數(shù)據(jù)增多了,是該加內(nèi)存還是加實例?采用云主機(jī)來運(yùn)行Redis實例,那么,該如何選擇云主機(jī)的內(nèi)存容量呢?用Redis保存5000萬個鍵值對,每個鍵值對大約是512B方案一:大內(nèi)存云主機(jī):選擇一臺32GB內(nèi)存的云主機(jī)來部署Redis

一.切片集群

Redis中,數(shù)據(jù)增多了,是該加內(nèi)存還是加實例?

采用云主機(jī)來運(yùn)行 Redis 實例,那么,該如何選擇云主機(jī)的內(nèi)存容量呢?

用 Redis 保存 5000 萬個鍵值對,每個鍵值對大約是 512B

方案一:大內(nèi)存云主機(jī):選擇一臺 32GB 內(nèi)存的云主機(jī)來部署 Redis。因為 32GB 的內(nèi)存能保存所有數(shù)據(jù),而且還留有 7GB,可以保證系統(tǒng)的正常運(yùn)行。同時,我還采用 RDB 對數(shù)據(jù)做持久化,以確保 Redis 實例故障后,還能從 RDB 恢復(fù)數(shù)據(jù)。

結(jié)果:Redis 的響應(yīng)有時會非常慢,使用 INFO 命令查看 Redis 的 latest_fork_usec 指標(biāo)值(表示最近一次 fork 的耗時),結(jié)果顯示這個指標(biāo)值特別高,快到秒級別了。這跟 Redis 的持久化機(jī)制有關(guān)系。在使用 RDB 進(jìn)行持久化時,Redis 會 fork 子進(jìn)程來完成,fork 操作的用時和 Redis 的數(shù)據(jù)量是正相關(guān)的,而 fork 在執(zhí)行時會阻塞主線程。數(shù)據(jù)量越大,fork 操作造成的主線程阻塞的時間越長。所以,在使用 RDB 對 25GB 的數(shù)據(jù)進(jìn)行持久化時,數(shù)據(jù)量較大,后臺運(yùn)行的子進(jìn)程在 fork 創(chuàng)建時阻塞了主線程,于是就導(dǎo)致 Redis 響應(yīng)變慢了。

方案二:Redis 的切片集群。雖然組建切片集群比較麻煩,但是它可以保存大量數(shù)據(jù),而且對 Redis 主線程的阻塞影響較小。

如果把 25GB 的數(shù)據(jù)平均分成 5 份(當(dāng)然,也可以不做均分),使用 5 個實例來保存,每個實例只需要保存 5GB 數(shù)據(jù)。如下圖所示:

那么,在切片集群中,實例在為 5GB 數(shù)據(jù)生成 RDB 時,數(shù)據(jù)量就小了很多,fork 子進(jìn)程一般不會給主線程帶來較長時間的阻塞。采用多個實例保存數(shù)據(jù)切片后,我們既能保存 25GB 數(shù)據(jù),又避免了 fork 子進(jìn)程阻塞主線程而導(dǎo)致的響應(yīng)突然變慢。

1.什么是切片集群?

切片集群,也叫分片集群,就是指啟動多個 Redis 實例組成一個集群,然后按照一定的規(guī)則,把收到的數(shù)據(jù)劃分成多份,每一份用一個實例來保存。

2.如何保存更多的數(shù)據(jù)?

2.1橫向擴(kuò)展與縱向擴(kuò)展

上邊案例中使用了大內(nèi)存云片機(jī)和切片集群的方法。這兩種方法分別對應(yīng)著Redis應(yīng)對的數(shù)據(jù)量增多的兩種方案:縱向擴(kuò)展(sclae up)和橫向擴(kuò)展(scale out).

  • 縱向擴(kuò)展:升級單個 Redis 實例的資源配置,包括增加內(nèi)存容量、增加磁盤容量、使用更高配置的 CPU。就像下圖中,原來的實例內(nèi)存是 8GB,硬盤是 50GB,縱向擴(kuò)展后,內(nèi)存增加到 24GB,磁盤增加到 150GB。
  • 橫向擴(kuò)展:橫向增加當(dāng)前 Redis 實例的個數(shù),就像下圖中,原來使用 1 個 8GB 內(nèi)存、50GB 磁盤的實例,現(xiàn)在使用三個相同配置的實例。

2.2橫向擴(kuò)展和縱向擴(kuò)展的優(yōu)缺點

2.2.1縱向擴(kuò)展

好處:實施起來簡單、直接。

潛在問題:

第一個問題是,當(dāng)使用 RDB 對數(shù)據(jù)進(jìn)行持久化時,如果數(shù)據(jù)量增加,需要的內(nèi)存也會增加,主線程 fork 子進(jìn)程時就可能會阻塞(比如剛剛的例子中的情況)。不過,如果你不要求持久化、保存 Redis 數(shù)據(jù),那么,縱向擴(kuò)展會是一個不錯的選擇。

第二個問題:縱向擴(kuò)展會受到硬件和成本的限制。這很容易理解,畢竟,把內(nèi)存從 32GB 擴(kuò)展到 64GB 還算容易,但是,要想擴(kuò)充到 1TB,就會面臨硬件容量和成本上的限制了。

2.2.2橫向擴(kuò)展

橫向擴(kuò)展是一個擴(kuò)展性更好的方案。要想保存更多的數(shù)據(jù),采用這種方案的話,只用增加 Redis 的實例個數(shù)就行了,不用擔(dān)心單個實例的硬件和成本限制。在面向百萬、千萬級別的用戶規(guī)模時,橫向擴(kuò)展的 Redis 切片集群會是一個非常好的選擇。

3.切片集群面臨兩大問題:

數(shù)據(jù)切片后,在多個實例之間如何分布?

客戶端怎么確定想要訪問的數(shù)據(jù)在哪個實例上?

3.1橫向擴(kuò)展:數(shù)據(jù)切片和實例的對應(yīng)分布關(guān)系

在切片集群中,數(shù)據(jù)需要分布在不同實例上,數(shù)據(jù)和實例之間如何對應(yīng)呢?

Redis Cluster 方案

在 Redis 3.0 之前,官方并沒有針對切片集群提供具體的方案。從 3.0 開始,官方提供了一個名為 Redis Cluster 的方案,用于實現(xiàn)切片集群。Redis Cluster 方案中就規(guī)定了數(shù)據(jù)和實例的對應(yīng)規(guī)則。

3.1.1什么是Redis Cluster?

Redis Cluster 方案采用哈希槽(Hash Slot),來處理數(shù)據(jù)和實例之間的映射關(guān)系。在 Redis Cluster 方案中,一個切片集群共有 16384 個哈希槽,這些哈希槽類似于數(shù)據(jù)分區(qū),每個鍵值對都會根據(jù)它的 key,被映射到一個哈希槽中。

具體的映射過程分為兩大步:首先根據(jù)鍵值對的 key,按照CRC16 算法計算一個 16 bit 的值;然后,再用這個 16bit 值對 16384 取模,得到 0~16383 范圍內(nèi)的模數(shù),每個模數(shù)代表一個相應(yīng)編號的哈希槽。

3.1.2哈希槽又是如何被映射到具體的 Redis 實例?

在部署 Redis Cluster 方案時,可以使用 cluster create 命令創(chuàng)建集群,此時,Redis 會自動把這些槽平均分布在集群實例上。例如,如果集群中有 N 個實例,那么,每個實例上的槽個數(shù)為 16384/N 個。

可以使用 cluster meet 命令手動建立實例間的連接,形成集群,再使用 cluster addslots 命令,指定每個實例上的哈希槽個數(shù)

舉個例子,假設(shè)集群中不同 Redis 實例的內(nèi)存大小配置不一,如果把哈希槽均分在各個實例上,在保存相同數(shù)量的鍵值對時,和內(nèi)存大的實例相比,內(nèi)存小的實例就會有更大的容量壓力。遇到這種情況時,你可以根據(jù)不同實例的資源配置情況,使用 cluster addslots 命令手動分配哈希槽。

示意圖中的切片集群一共有 3 個實例,同時假設(shè)有 5 個哈希槽,我們首先可以通過下面的命令手動分配哈希槽:實例 1 保存哈希槽 0 和 1,實例 2 保存哈希槽 2 和 3,實例 3 保存哈希槽 4。

redis-cli -h 172.16.19.3 –p 6379 cluster addslots 0,1
redis-cli -h 172.16.19.4 –p 6379 cluster addslots 2,3
redis-cli -h 172.16.19.5 –p 6379 cluster addslots 4

在集群運(yùn)行的過程中,key1 和 key2 計算完 CRC16 值后,對哈希槽總個數(shù) 5 取模,再根據(jù)各自的模數(shù)結(jié)果,就可以被映射到對應(yīng)的實例 1 和實例 3 上了。

注意:在手動分配哈希槽時,需要把 16384 個槽都分配完,否則 Redis 集群無法正常工作。

切片集群就實現(xiàn)了數(shù)據(jù)到哈希槽、哈希槽再到實例的分配

即使實例有了哈希槽的映射信息,客戶端又是怎么知道要訪問的數(shù)據(jù)在哪個實例上呢?

3.2客戶端如何定位數(shù)據(jù)?

在定位鍵值對數(shù)據(jù)時,它所處的哈希槽slots是可以通過計算得到的,這個計算可以在客戶端發(fā)送請求時來執(zhí)行。但是,要進(jìn)一步定位到實例,還需要知道哈希槽分布在哪個實例上。

一般來說,客戶端和集群實例建立連接后,實例就會把哈希槽的分配信息發(fā)給客戶端。但是,在集群剛剛創(chuàng)建的時候,每個實例只知道自己被分配了哪些哈希槽,是不知道其他實例擁有的哈希槽信息。

客戶端為什么可以在訪問任何一個實例時,都能獲得所有的哈希槽信息呢?

Redis 實例會把自己的哈希槽信息發(fā)給和它相連接的其它實例,來完成哈希槽分配信息的擴(kuò)散。當(dāng)實例之間相互連接后,每個實例就有所有哈希槽的映射關(guān)系了??蛻舳耸盏焦2坌畔⒑螅瑫压2坌畔⒕彺嬖诒镜?。當(dāng)客戶端請求鍵值對時,會先計算鍵所對應(yīng)的哈希槽,然后就可以給相應(yīng)的實例發(fā)送請求了。

在集群中,實例和哈希槽的對應(yīng)關(guān)系并不是一成不變的,最常見的變化有兩個:

  • 在集群中,實例有新增或刪除,Redis 需要重新分配哈希槽;
  • 為了負(fù)載均衡,Redis 需要把哈希槽在所有實例上重新分布一遍。

實例之間還可以通過相互傳遞消息,獲得最新的哈希槽分配信息

客戶端是無法主動感知這些變化的。這就會導(dǎo)致,它緩存的分配信息和最新的分配信息就不一致了,那該怎么辦呢?

Redis Cluster 方案提供了一種重定向機(jī)制,就是指,客戶端給一個實例發(fā)送數(shù)據(jù)讀寫操作時,這個實例上并沒有相應(yīng)的數(shù)據(jù),客戶端要再給一個新實例發(fā)送操作命令。

3.2.1MOVED 重定向命令的使用方法

客戶端怎么知道重定向時新實例訪問地址?客戶端端請求到了一個不包含 key 對應(yīng)的哈希槽,集群將做何響應(yīng)?

當(dāng)客戶端把一個鍵值對的操作請求發(fā)送給一個實例,這個實例上沒有鍵值對映射的哈希槽,這個實例就會給客戶端返回MOVED命令響應(yīng)結(jié)果,結(jié)果中就包含了新實例的訪問地址。

get hello:key
(error)MOVED 13320 172.16.19.5:6379

MOVED 命令表示,客戶端請求的鍵值對所在的哈希槽 13320,實際是在 172.16.19.5 這個實例上。通過返回的 MOVED 命令,就相當(dāng)于把哈希槽所在的新實例的信息告訴給客戶端了。這樣一來,客戶端就可以直接和 172.16.19.5 連接,并發(fā)送操作請求了。

由于負(fù)載均衡,Slot 2 中的數(shù)據(jù)已經(jīng)從實例 2 遷移到了實例 3,但是,客戶端緩存仍然記錄著“Slot 2 在實例 2”的信息,所以會給實例 2 發(fā)送命令。實例 2 給客戶端返回一條 MOVED 命令,把 Slot 2 的最新位置(也就是在實例 3 上),返回給客戶端,客戶端就會再次向?qū)嵗?3 發(fā)送請求,同時還會更新本地緩存,把 Slot 2 與實例的對應(yīng)關(guān)系更新過來

3.2.2ASK命令使用方法

可能會出現(xiàn)這樣一種情況:屬于被遷移槽的一部分鍵值對保存在源節(jié)點里面,而另一部分鍵值對則保存在目標(biāo)節(jié)點里面。

例如:客戶端向?qū)嵗?2 發(fā)送請求,但此時,Slot 2 中的數(shù)據(jù)只有一部分遷移到了實例 3,還有部分?jǐn)?shù)據(jù)沒有遷移。在這種遷移部分完成的情況下,客戶端就會收到一條 ASK 報錯信息,如下所示:

Get hello:key
(error)ASK 13320 172.16.19.5:6379

這個結(jié)果中的 ASK 命令就表示,客戶端請求的鍵值對所在的哈希槽 13320,在 172.16.19.5 這個實例上,但是這個哈希槽正在遷移。此時,客戶端需要先給 172.16.19.5 這個實例發(fā)送一個 ASKING 命令。這個命令的意思是,讓這個實例允許執(zhí)行客戶端接下來發(fā)送的命令。然后,客戶端再向這個實例發(fā)送 GET 命令,以讀取數(shù)據(jù).

ASK 命令表示兩層含義:第一,表明 Slot 數(shù)據(jù)還在遷移中;第二,ASK 命令把客戶端所請求數(shù)據(jù)的最新實例地址返回給客戶端,此時,客戶端需要給實例 3 發(fā)送 ASKING 命令,然后再發(fā)送操作命令

3.2.3MOVED命令和ASK命令區(qū)別

  • MOVED命令會更新客戶端緩存的哈希槽分配信息,ASK不會更新客戶端緩存。如果客戶端再次請求 Slot 2 中的數(shù)據(jù),它還是會給實例 2 發(fā)送請求。
  • ASK命令作用只是讓客戶端能給新實例發(fā)送一次請求,而MOVED命令修改本地緩存,讓后續(xù)命令發(fā)往新實例。

3.切片集群總結(jié)

本篇主要講述了,切片集群在保存大量數(shù)據(jù)方面的優(yōu)勢,以及基于哈希槽的數(shù)據(jù)分布機(jī)制和客戶端定位鍵值對的方法。

  • 在應(yīng)對數(shù)據(jù)量大的數(shù)據(jù),數(shù)據(jù)擴(kuò)容時,雖然增加內(nèi)存這種縱向擴(kuò)展的方式簡單直接,但是會造成內(nèi)存過大,導(dǎo)致性能變慢。同事也受到硬件和成本的限制。
  • Redis切片集群提供了橫向擴(kuò)展的模式,也就是使用多個實例,并給每個實例分配一定的哈希槽,數(shù)據(jù)可以通過鍵的哈希值映射到哈希槽,在通過哈希槽分散分布在不同的實例上。擴(kuò)展性好,通過增加實例可以存儲大量數(shù)據(jù)。
  • 集群是實例的增減和為了實現(xiàn)負(fù)載均衡而進(jìn)行的數(shù)據(jù)重新分布,導(dǎo)致哈希槽和實例映射關(guān)系的變化,客戶端請求時,會收到命令執(zhí)行報錯信息。MOVED 和 ASK 命令,讓客戶端獲取最新信息。
  • 在 Redis 3.0 之前,Redis 官方并沒有提供切片集群方案,但是,其實當(dāng)時業(yè)界已經(jīng)有了一些切片集群的方案,例如基于客戶端分區(qū)的 ShardedJedis,基于代理的 Codis、Twemproxy 等。這些方案的應(yīng)用早于 Redis Cluster 方案

Redis Cluster 方案通過哈希槽的方式把鍵值對分配到不同的實例上,這個過程需要對鍵值對的 key 做 CRC 計算,然后再和哈希槽做映射,這樣做有什么好處嗎?如果用一個表直接把鍵值對和實例的對應(yīng)關(guān)系記錄下來(例如鍵值對 1 在實例 2 上,鍵值對 2 在實例 1 上),這樣就不用計算 key 和哈希槽的對應(yīng)關(guān)系了,只用查表就行了,Redis 為什么不這么做呢?

1、整個集群存儲key的數(shù)量是無法預(yù)估的,key的數(shù)量非常多時,直接記錄每個key對應(yīng)的實例映射關(guān)系,這個映射表會非常龐大,這個映射表無論是存儲在服務(wù)端還是客戶端都占用了非常大的內(nèi)存空間。

2、Redis Cluster采用無中心化的模式(無proxy,客戶端與服務(wù)端直連),客戶端在某個節(jié)點訪問一個key,如果這個key不在這個節(jié)點上,這個節(jié)點需要有糾正客戶端路由到正確節(jié)點的能力(MOVED響應(yīng)),這就需要節(jié)點之間互相交換路由表,每個節(jié)點擁有整個集群完整的路由關(guān)系。如果存儲的都是key與實例的對應(yīng)關(guān)系,節(jié)點之間交換信息也會變得非常龐大,消耗過多的網(wǎng)絡(luò)資源,而且就算交換完成,相當(dāng)于每個節(jié)點都需要額外存儲其他節(jié)點的路由表,內(nèi)存占用過大造成資源浪費。

3、當(dāng)集群在擴(kuò)容、縮容、數(shù)據(jù)均衡時,節(jié)點之間會發(fā)生數(shù)據(jù)遷移,遷移時需要修改每個key的映射關(guān)系,維護(hù)成本高。

4、而在中間增加一層哈希槽,可以把數(shù)據(jù)和節(jié)點解耦,key通過Hash計算,只需要關(guān)心映射到了哪個哈希槽,然后再通過哈希槽和節(jié)點的映射表找到節(jié)點,相當(dāng)于消耗了很少的CPU資源,不但讓數(shù)據(jù)分布更均勻,還可以讓這個映射表變得很小,利于客戶端和服務(wù)端保存,節(jié)點之間交換信息時也變得輕量。

5、當(dāng)集群在擴(kuò)容、縮容、數(shù)據(jù)均衡時,節(jié)點之間的操作例如數(shù)據(jù)遷移,都以哈希槽為基本單位進(jìn)行操作,簡化了節(jié)點擴(kuò)容、縮容的難度,便于集群的維護(hù)和管理。

請求路由、數(shù)據(jù)遷移

Redis使用集群方案就是為了解決單個節(jié)點數(shù)據(jù)量大、寫入量大產(chǎn)生的性能瓶頸的問題。多個節(jié)點組成一個集群,可以提高集群的性能和可靠性,但隨之而來的就是集群的管理問題,最核心問題有2個:請求路由、數(shù)據(jù)遷移(擴(kuò)容/縮容/數(shù)據(jù)平衡)。

1、請求路由:一般都是采用哈希槽的映射關(guān)系表找到指定節(jié)點,然后在這個節(jié)點上操作的方案。

Redis Cluster在每個節(jié)點記錄完整的映射關(guān)系(便于糾正客戶端的錯誤路由請求),同時也發(fā)給客戶端讓客戶端緩存一份,便于客戶端直接找到指定節(jié)點,客戶端與服務(wù)端配合完成數(shù)據(jù)的路由,這需要業(yè)務(wù)在使用Redis Cluster時,必須升級為集群版的SDK才支持客戶端和服務(wù)端的協(xié)議交互。

其他Redis集群化方案例如Twemproxy、Codis都是中心化模式(增加Proxy層),客戶端通過Proxy對整個集群進(jìn)行操作,Proxy后面可以掛N多個Redis實例,Proxy層維護(hù)了路由的轉(zhuǎn)發(fā)邏輯。操作Proxy就像是操作一個普通Redis一樣,客戶端也不需要更換SDK,而Redis Cluster是把這些路由邏輯做在了SDK中。當(dāng)然,增加一層Proxy也會帶來一定的性能損耗。

2、數(shù)據(jù)遷移:當(dāng)集群節(jié)點不足以支撐業(yè)務(wù)需求時,就需要擴(kuò)容節(jié)點,擴(kuò)容就意味著節(jié)點之間的數(shù)據(jù)需要做遷移,而遷移過程中是否會影響到業(yè)務(wù),這也是判定一個集群方案是否成熟的標(biāo)準(zhǔn)。

Twemproxy不支持在線擴(kuò)容,它只解決了請求路由的問題,擴(kuò)容時需要停機(jī)做數(shù)據(jù)重新分配。而Redis Cluster和Codis都做到了在線擴(kuò)容(不影響業(yè)務(wù)或?qū)I(yè)務(wù)的影響非常?。?,重點就是在數(shù)據(jù)遷移過程中,客戶端對于正在遷移的key進(jìn)行操作時,集群如何處理?還要保證響應(yīng)正確的結(jié)果?

Redis Cluster和Codis都需要服務(wù)端和客戶端/Proxy層互相配合,遷移過程中,服務(wù)端針對正在遷移的key,需要讓客戶端或Proxy去新節(jié)點訪問(重定向),這個過程就是為了保證業(yè)務(wù)在訪問這些key時依舊不受影響,而且可以得到正確的結(jié)果。由于重定向的存在,所以這個期間的訪問延遲會變大。等遷移完成之后,Redis Cluster每個節(jié)點會更新路由映射表,同時也會讓客戶端感知到,更新客戶端緩存。Codis會在Proxy層更新路由表,客戶端在整個過程中無感知。

除了訪問正確的節(jié)點之外,數(shù)據(jù)遷移過程中還需要解決異常情況(遷移超時、遷移失?。?、性能問題(如何讓數(shù)據(jù)遷移更快、bigkey如何處理),這個過程中的細(xì)節(jié)也很多。

Redis Cluster的數(shù)據(jù)遷移是同步的,遷移一個key會同時阻塞源節(jié)點和目標(biāo)節(jié)點,遷移過程中會有性能問題。而Codis提供了異步遷移數(shù)據(jù)的方案,遷移速度更快,對性能影響最小,當(dāng)然,實現(xiàn)方案也比較復(fù)雜。

二:切片集群方案:Codis \ Redis Cluster

Redis 官方提供的切片集群方案 Redis Cluster。但是Redis Cluster 方案正式發(fā)布前,業(yè)界已經(jīng)廣泛使用的 Codis值得關(guān)注

1.Codis 的整體架構(gòu)和基本流程

Codis 集群中包含了 4 類關(guān)鍵組件。

  • codis server:這是進(jìn)行了二次開發(fā)的 Redis 實例,其中增加了額外的數(shù)據(jù)結(jié)構(gòu),支持?jǐn)?shù)據(jù)遷移操作,主要負(fù)責(zé)處理具體的數(shù)據(jù)讀寫請求。
  • codis proxy:接收客戶端請求,并把請求轉(zhuǎn)發(fā)給 codis server。
  • Zookeeper 集群:保存集群元數(shù)據(jù),例如數(shù)據(jù)位置信息和 codis proxy 信息。
  • codis dashboard 和 codis fe:共同組成了集群管理工具。其中,codis dashboard 負(fù)責(zé)執(zhí)行集群管理工作,包括增刪 codis server、codis proxy 和進(jìn)行數(shù)據(jù)遷移。而 codis fe 負(fù)責(zé)提供 dashboard 的 Web 操作界面,便于我們直接在 Web 界面上進(jìn)行集群管理。

1.1Codis 是如何處理請求的

首先--》為了讓集群能接收并處理請求:先使用 codis dashboard 設(shè)置 codis server 和 codis proxy 的訪問地址,完成設(shè)置后,codis server 和 codis proxy 才會開始接收連接。

其次--》當(dāng)客戶端要讀寫數(shù)據(jù)時,客戶端直接和 codis proxy 建立連接.codis proxy 本身支持 Redis 的 RESP 交互協(xié)議.客戶端訪問 codis proxy 時,和訪問原生的 Redis 實例沒有什么區(qū)別。原本連接單實例的客戶端就可以輕松地和 Codis 集群建立起連接。

最后--》codis proxy 接收到請求,就會查詢請求數(shù)據(jù)和 codis server 的映射關(guān)系,并把請求轉(zhuǎn)發(fā)給相應(yīng)的 codis server 進(jìn)行處理。當(dāng) codis server 處理完請求后,會把結(jié)果返回給 codis proxy,proxy 再把數(shù)據(jù)返回給客戶端。

2.Codis的關(guān)鍵技術(shù)原理

2.1數(shù)據(jù)如何在集群里分布

在 Codis 集群中,一個數(shù)據(jù)應(yīng)該保存在哪個 codis server 上,這是通過邏輯槽(Slot)映射來完成的,具體來說,總共分成兩步。

第一步,Codis 集群一共有 1024 個 Slot,編號依次是 0 到 1023。把這些 Slot 手動分配給 codis server,每個 server 上包含一部分 Slot。也可以讓 codis dashboard 進(jìn)行自動分配,例如,dashboard 把 1024 個 Slot 在所有 server 上均分。

第二步,當(dāng)客戶端要讀寫數(shù)據(jù)時,會使用 CRC32 算法計算數(shù)據(jù) key 的哈希值,并把這個哈希值對 1024 取模。而取模后的值,則對應(yīng) Slot 的編號。此時,根據(jù)第一步分配的 Slot 和 server 對應(yīng)關(guān)系,可以知道數(shù)據(jù)保存在哪個 server 上了。

2.2例子

下圖顯示的就是數(shù)據(jù)、Slot 和 codis server 的映射保存關(guān)系。其中,Slot 0 和 1 被分配到了 server1,Slot 2 分配到 server2,Slot 1022 和 1023 被分配到 server8。當(dāng)客戶端訪問 key 1 和 key 2 時,這兩個數(shù)據(jù)的 CRC32 值對 1024 取模后,分別是 1 和 1022。因此,它們會被保存在 Slot 1 和 Slot 1022 上,而 Slot 1 和 Slot 1022 已經(jīng)被分配到 codis server 1 和 8 上了。key 1 和 key 2 的保存位置就很清楚。

數(shù)據(jù) key 和 Slot 的映射關(guān)系是客戶端在讀寫數(shù)據(jù)前直接通過 CRC32 計算得到的,而 Slot 和 codis server 的映射關(guān)系是通過分配完成的,所以就需要用一個存儲系統(tǒng)保存下來,否則,如果集群有故障了,映射關(guān)系就會丟失。

把 Slot 和 codis server 的映射關(guān)系稱為數(shù)據(jù)路由表(簡稱路由表)。我們在 codis dashboard 上分配好路由表后,dashboard 會把路由表發(fā)送給 codis proxy,同時,dashboard 也會把路由表保存在 Zookeeper 中。codis-proxy 會把路由表緩存在本地,當(dāng)它接收到客戶端請求后,直接查詢本地的路由表,就可以完成正確的請求轉(zhuǎn)發(fā)了

在數(shù)據(jù)分布的實現(xiàn)方法上,Codis 和 Redis Cluster 很相似,都采用了 key 映射到 Slot、Slot 再分配到實例上的機(jī)制

2.3Codis 和 Redis Cluster數(shù)據(jù)分布上的區(qū)別

  • Codis 中的路由表:通過 codis dashboard 分配和修改的,并被保存在 Zookeeper 集群中。一旦數(shù)據(jù)位置發(fā)生變化(例如有實例增減),路由表被修改了,codis dashbaord 就會把修改后的路由表發(fā)送給 codis proxy,proxy 就可以根據(jù)最新的路由信息轉(zhuǎn)發(fā)請求了。
  • 在 Redis Cluster 中,數(shù)據(jù)路由表是通過每個實例相互間的通信傳遞的,最后會在每個實例上保存一份。當(dāng)數(shù)據(jù)路由信息發(fā)生變化時,就需要在所有實例間通過網(wǎng)絡(luò)消息進(jìn)行傳遞。所以,如果實例數(shù)量較多的話,就會消耗較多的集群網(wǎng)絡(luò)資源。

3.集群擴(kuò)容和數(shù)據(jù)遷移

Codis 集群擴(kuò)容包括了兩方面:增加 codis server 和增加 codis proxy。

3.1增加codis server

兩步操作:

  1. 啟動新的 codis server,將它加入集群;
  2. 把部分?jǐn)?shù)據(jù)遷移到新的 server。

3.1.1數(shù)據(jù)遷移的基本流程

Codis 集群按照 Slot 的粒度進(jìn)行數(shù)據(jù)遷移,數(shù)據(jù)遷移是一個重要的機(jī)制

  1. 在源 server 上,Codis 從要遷移的 Slot 中隨機(jī)選擇一個數(shù)據(jù),發(fā)送給目的 server。
  2. 目的 server 確認(rèn)收到數(shù)據(jù)后,會給源 server 返回確認(rèn)消息。這時,源 server 會在本地將剛才遷移的數(shù)據(jù)刪除。
  3. 第一步和第二步就是單個數(shù)據(jù)的遷移過程。Codis 會不斷重復(fù)這個遷移過程,直到要遷移的 Slot 中的數(shù)據(jù)全部遷移完成。

Codis 實現(xiàn)了兩種遷移模式,分別是同步遷移和異步遷移

3.1.2同步遷移

同步遷移是指,在數(shù)據(jù)從源 server 發(fā)送給目的 server 的過程中,源 server 是阻塞的,無法處理新的請求操作。這種模式很容易實現(xiàn),但是遷移過程中會涉及多個操作(包括數(shù)據(jù)在源 server 序列化、網(wǎng)絡(luò)傳輸、在目的 server 反序列化,以及在源 server 刪除),如果遷移的數(shù)據(jù)是一個 bigkey,源 server 就會阻塞較長時間,無法及時處理用戶請求。

3.1.3異步遷移

為了避免數(shù)據(jù)遷移阻塞源 server,Codis 實現(xiàn)的第二種遷移模式就是異步遷移

異步遷移的兩個關(guān)鍵特點

第一個特點是:

  • 當(dāng)源 server 把數(shù)據(jù)發(fā)送給目的 server 后,就可以處理其他請求操作了,不用等到目的 server 的命令執(zhí)行完。而目的 server 會在收到數(shù)據(jù)并反序列化保存到本地后,給源 server 發(fā)送一個 ACK 消息,表明遷移完成。此時,源 server 在本地把剛才遷移的數(shù)據(jù)刪除。
  • 在這個過程中,遷移的數(shù)據(jù)會被設(shè)置為只讀,所以,源 server 上的數(shù)據(jù)不會被修改,自然也就不會出現(xiàn)“和目的 server 上的數(shù)據(jù)不一致”的問題了

第二個特點是:

  • 于 bigkey,異步遷移采用了拆分指令的方式進(jìn)行遷移。具體來說就是,對 bigkey 中每個元素,用一條指令進(jìn)行遷移,而不是把整個 bigkey 進(jìn)行序列化后再整體傳輸。這種化整為零的方式,就避免了 bigkey 遷移時,因為要序列化大量數(shù)據(jù)而阻塞源 server 的問題。
  • 當(dāng) bigkey 遷移了一部分?jǐn)?shù)據(jù)后,如果 Codis 發(fā)生故障,就會導(dǎo)致 bigkey 的一部分元素在源 server,而另一部分元素在目的 server,這就破壞了遷移的原子性。所以,Codis 會在目標(biāo) server 上,給 bigkey 的元素設(shè)置一個臨時過期時間。如果遷移過程中發(fā)生故障,那么,目標(biāo) server 上的 key 會在過期后被刪除,不會影響遷移的原子性。當(dāng)正常完成遷移后,bigkey 元素的臨時過期時間會被刪除。

第二個特點例子:

  • 假如要遷移一個有 1 萬個元素的 List 類型數(shù)據(jù),當(dāng)使用異步遷移時,源 server 就會給目的 server 傳輸 1 萬條 RPUSH 命令,每條命令對應(yīng)了 List 中一個元素的插入。在目的 server 上,這 1 萬條命令再被依次執(zhí)行,就可以完成數(shù)據(jù)遷移。
  • 為了提升遷移的效率,Codis 在異步遷移 Slot 時,允許每次遷移多個 key。可以通過異步遷移命令 SLOTSMGRTTAGSLOT-ASYNC 的參數(shù) numkeys 設(shè)置每次遷移的 key 數(shù)量

3.2增加codis proxy

Codis 集群中,客戶端是和 codis proxy 直接連接的,所以,當(dāng)客戶端增加時,一個 proxy 無法支撐大量的請求操作,就需要增加 proxy。增加 proxy 比較容易,直接啟動 proxy,再通過 codis dashboard 把 proxy 加入集群就行。

此時,codis proxy 的訪問連接信息都會保存在 Zookeeper 上。所以,當(dāng)新增了 proxy 后,Zookeeper 上會有最新的訪問列表,客戶端也就可以從 Zookeeper 上讀取 proxy 訪問列表,把請求發(fā)送給新增的 proxy。這樣一來,客戶端的訪問壓力就可以在多個 proxy 上分擔(dān)處理了,如下圖所示

4:客戶端能否與集群直接交互

使用 Redis 單實例時,客戶端只要符合 RESP 協(xié)議,就可以和實例進(jìn)行交互和讀寫數(shù)據(jù)。但是,在使用切片集群時,有些功能是和單實例不一樣的,比如集群中的數(shù)據(jù)遷移操作,在單實例上是沒有的,而且遷移過程中,數(shù)據(jù)訪問請求可能要被重定向(例如 Redis Cluster 中的 MOVE 命令)。

客戶端需要增加和集群功能相關(guān)的命令操作的支持。如果原來使用單實例客戶端,想要擴(kuò)容使用集群,就需要使用新客戶端,這對于業(yè)務(wù)應(yīng)用的兼容性來說,并不是特別友好。

Codis 集群在設(shè)計時,就充分考慮了對現(xiàn)有單實例客戶端的兼容性。

Codis 使用 codis proxy 直接和客戶端連接,codis proxy 是和單實例客戶端兼容的。而和集群相關(guān)的管理工作(例如請求轉(zhuǎn)發(fā)、數(shù)據(jù)遷移等),都由 codis proxy、codis dashboard 這些組件來完成,不需要客戶端參與。

業(yè)務(wù)應(yīng)用使用 Codis 集群時,就不用修改客戶端了,可以復(fù)用和單實例連接的客戶端,既能利用集群讀寫大容量數(shù)據(jù),又避免了修改客戶端增加復(fù)雜的操作邏輯,保證了業(yè)務(wù)代碼的穩(wěn)定性和兼容性。

5.怎么保證集群可靠性?

可靠性是實際業(yè)務(wù)應(yīng)用的一個核心要求。對于一個分布式系統(tǒng)來說,它的可靠性和系統(tǒng)中的組件個數(shù)有關(guān):組件越多,潛在的風(fēng)險點也就越多。

和 Redis Cluster 只包含 Redis 實例不一樣,Codis 集群包含的組件有 4 類

Codis 不同組件的可靠性保證方法。

5.1codis server保證可靠性方法

  1. codis server 其實就是 Redis 實例,只不過增加了和集群操作相關(guān)的命令。Redis 的主從復(fù)制機(jī)制和哨兵機(jī)制在 codis server 上都是可以使用的,所以,Codis 就使用主從集群來保證 codis server 的可靠性。簡單來說就是,Codis 給每個 server 配置從庫,并使用哨兵機(jī)制進(jìn)行監(jiān)控,當(dāng)發(fā)生故障時,主從庫可以進(jìn)行切換,從而保證了 server 的可靠性。
  2. 在這種配置情況下,每個 server 就成為了一個 server group,每個 group 中是一主多從的 server。數(shù)據(jù)分布使用的 Slot,也是按照 group 的粒度進(jìn)行分配的。同時,codis proxy 在轉(zhuǎn)發(fā)請求時,也是按照數(shù)據(jù)所在的 Slot 和 group 的對應(yīng)關(guān)系,把寫請求發(fā)到相應(yīng) group 的主庫,讀請求發(fā)到 group 中的主庫或從庫上。

下圖展示的是配置了 server group 的 Codis 集群架構(gòu)。在 Codis 集群中,我們通過部署 server group 和哨兵集群,實現(xiàn) codis server 的主從切換,提升集群可靠性。

5.2codis proxy 和 Zookeeper可靠性

  • 在 Codis 集群設(shè)計時,proxy 上的信息源頭都是來自 Zookeeper(例如路由表)。而 Zookeeper 集群使用多個實例來保存數(shù)據(jù),只要有超過半數(shù)的 Zookeeper 實例可以正常工作, Zookeeper 集群就可以提供服務(wù),也可以保證這些數(shù)據(jù)的可靠性。
  • 所以,codis proxy 使用 Zookeeper 集群保存路由表,可以充分利用 Zookeeper 的高可靠性保證來確保 codis proxy 的可靠性,不用再做額外的工作了。當(dāng) codis proxy 發(fā)生故障后,直接重啟 proxy 就行。重啟后的 proxy,可以通過 codis dashboard 從 Zookeeper 集群上獲取路由表,然后,就可以接收客戶端請求進(jìn)行轉(zhuǎn)發(fā)了。這樣的設(shè)計,也降低了 Codis 集群本身的開發(fā)復(fù)雜度。

5.3codis dashboard 和 codis fe可靠性

它們主要提供配置管理和管理員手工操作,負(fù)載壓力不大,所以,它們的可靠性可以不用額外進(jìn)行保證了

6.切片集群方案選擇建議

6.1Codis 和 Redis Cluster 區(qū)別

6.2實際應(yīng)用中的兩種方案

  • 從穩(wěn)定性和成熟度來看,Codis 應(yīng)用得比較早,在業(yè)界已經(jīng)有了成熟的生產(chǎn)部署。雖然 Codis 引入了 proxy 和 Zookeeper,增加了集群復(fù)雜度,但是,proxy 的無狀態(tài)設(shè)計和 Zookeeper 自身的穩(wěn)定性,也給 Codis 的穩(wěn)定使用提供了保證。而 Redis Cluster 的推出時間晚于 Codis,相對來說,成熟度要弱于 Codis,如果你想選擇一個成熟穩(wěn)定的方案,Codis 更加合適些。
  • 從業(yè)務(wù)應(yīng)用客戶端兼容性來看,連接單實例的客戶端可以直接連接 codis proxy,而原本連接單實例的客戶端要想連接 Redis Cluster 的話,就需要開發(fā)新功能。所以,如果你的業(yè)務(wù)應(yīng)用中大量使用了單實例的客戶端,而現(xiàn)在想應(yīng)用切片集群的話,建議你選擇 Codis,這樣可以避免修改業(yè)務(wù)應(yīng)用中的客戶端。
  • 從使用 Redis 新命令和新特性來看,Codis server 是基于開源的 Redis 3.2.8 開發(fā)的,所以,Codis 并不支持 Redis 后續(xù)的開源版本中的新增命令和數(shù)據(jù)類型。另外,Codis 并沒有實現(xiàn)開源 Redis 版本的所有命令,比如 BITOP、BLPOP、BRPOP,以及和與事務(wù)相關(guān)的 MUTLI、EXEC 等命令。Codis 官網(wǎng)上列出了不被支持的命令列表,你在使用時記得去核查一下。所以,如果你想使用開源 Redis 版本的新特性,Redis Cluster 是一個合適的選擇。
  • 從數(shù)據(jù)遷移性能維度來看,Codis 能支持異步遷移,異步遷移對集群處理正常請求的性能影響要比使用同步遷移的小。所以,如果你在應(yīng)用集群時,數(shù)據(jù)遷移比較頻繁的話,Codis 是個更合適的選擇。

7.Codis 和 Redis Cluster總結(jié)

Codis 集群包含 codis server、codis proxy、Zookeeper、codis dashboard 和 codis fe 這四大類組件。

  1. codis proxy 和 codis server 負(fù)責(zé)處理數(shù)據(jù)讀寫請求,其中,codis proxy 和客戶端連接,接收請求,并轉(zhuǎn)發(fā)請求給 codis server,而 codis server 負(fù)責(zé)具體處理請求。
  2. codis dashboard 和 codis fe 負(fù)責(zé)集群管理,其中,codis dashboard 執(zhí)行管理操作,而 codis fe 提供 Web 管理界面。
  3. Zookeeper 集群負(fù)責(zé)保存集群的所有元數(shù)據(jù)信息,包括路由表、proxy 實例信息等。這里,有個地方需要你注意,除了使用 Zookeeper,Codis 還可以使用 etcd 或本地文件系統(tǒng)保存元數(shù)據(jù)信息。

Codis 使用上的小建議:當(dāng)你有多條業(yè)務(wù)線要使用 Codis 時,可以啟動多個 codis dashboard,每個 dashboard 管理一部分 codis server,同時,再用一個 dashboard 對應(yīng)負(fù)責(zé)一個業(yè)務(wù)線的集群管理,這樣,就可以做到用一個 Codis 集群實現(xiàn)多條業(yè)務(wù)線的隔離管理了。

假設(shè) Codis 集群中保存的 80% 的鍵值對都是 Hash 類型,每個 Hash 集合的元素數(shù)量在 10 萬~20 萬個,每個集合元素的大小是 2KB。你覺得,遷移一個這樣的 Hash 集合數(shù)據(jù),會對 Codis 的性能造成影響嗎?

Codis 在遷移數(shù)據(jù)時,設(shè)計的方案可以保證遷移性能不受影響。

  • 1、異步遷移:源節(jié)點把遷移的數(shù)據(jù)發(fā)送給目標(biāo)節(jié)點后就返回,之后接著處理客戶端請求,這個階段不會長時間阻塞源節(jié)點。目標(biāo)節(jié)點加載遷移的數(shù)據(jù)成功后,向源節(jié)點發(fā)送 ACK 命令,告知其遷移成功。
  • 2、源節(jié)點異步釋放 key:源節(jié)點收到目標(biāo)節(jié)點 ACK 后,在源實例刪除這個 key,釋放 key 內(nèi)存的操作,會放到后臺線程中執(zhí)行,不會阻塞源實例。(沒錯,Codis 比 Redis 更早地支持了 lazy-free,只不過只用在了數(shù)據(jù)遷移中)。
  • 3、小對象序列化傳輸:小對象依舊采用序列化方式遷移,節(jié)省網(wǎng)絡(luò)流量。
  • 4、bigkey 分批遷移:bigkey 拆分成一條條命令,打包分批遷移(利用了 Pipeline 的優(yōu)勢),提升遷移速度。
  • 5、一次遷移多個 key:一次發(fā)送多個 key 進(jìn)行遷移,提升遷移效率。
  • 6、遷移流量控制:遷移時會控制緩沖區(qū)大小,避免占滿網(wǎng)絡(luò)帶寬。
  • 7、bigkey 遷移原子性保證(兼容遷移失敗情況):遷移前先發(fā)一個 DEL 命令到目標(biāo)節(jié)點(重試可保證冪等性),然后把 bigkey 拆分成一條條命令,并設(shè)置一個臨時過期時間(防止遷移失敗在目標(biāo)節(jié)點遺留垃圾數(shù)據(jù)),遷移成功后在目標(biāo)節(jié)點設(shè)置真實的過期時間。 Codis 在數(shù)據(jù)遷移方面要比 Redis Cluster 做得更優(yōu)秀,而且 Codis 還帶了一個非常友好的運(yùn)維界面,方便 DBA 執(zhí)行增刪節(jié)點、主從切換、數(shù)據(jù)遷移等操作。

三.通信開銷:限制Redis Cluster規(guī)模的關(guān)鍵因素

1:為什么要限定集群規(guī)模呢?

Redis Cluster 能保存的數(shù)據(jù)量以及支撐的吞吐量,跟集群的實例規(guī)模密切相關(guān)。Redis 官方給出了 Redis Cluster 的規(guī)模上限,就是一個集群運(yùn)行 1000 個實例

這里的一個關(guān)鍵因素就是,實例間的通信開銷會隨著實例規(guī)模增加而增大,在集群超過一定規(guī)模時(比如 800 節(jié)點),集群吞吐量反而會下降。所以,集群的實際規(guī)模會受到限制

2:實例通信方法和對集群規(guī)模的影響

Redis Cluster 在運(yùn)行時,每個實例上都會保存 Slot 和實例的對應(yīng)關(guān)系(也就是 Slot 映射表),以及自身的狀態(tài)信息。

為了讓集群中的每個實例都知道其它所有實例的狀態(tài)信息,實例之間會按照一定的規(guī)則進(jìn)行通信。這個規(guī)則就是 Gossip 協(xié)議

2.1Gossip 協(xié)議

Gossip 協(xié)議的工作原理可以概括成兩點。:檢測實例時候在線\給發(fā)送PING命令實例返回PONG消息

  1. 一是,每個實例之間會按照一定的頻率,從集群中隨機(jī)挑選一些實例,把 PING 消息發(fā)送給挑選出來的實例,用來檢測這些實例是否在線,并交換彼此的狀態(tài)信息。PING 消息中封裝了發(fā)送消息的實例自身的狀態(tài)信息、部分其它實例的狀態(tài)信息,以及 Slot 映射表。
  2. 二是,一個實例在接收到 PING 消息后,會給發(fā)送 PING 消息的實例,發(fā)送一個 PONG 消息。PONG 消息包含的內(nèi)容和 PING 消息一樣。

Gossip 協(xié)議可以保證在一段時間后,集群中的每一個實例都能獲得其它所有實例的狀態(tài)信息。

這樣一來,即使有新節(jié)點加入、節(jié)點故障、Slot 變更等事件發(fā)生,實例間也可以通過 PING、PONG 消息的傳遞,完成集群狀態(tài)在每個實例上的同步

3.通信的影響

實例間使用 Gossip 協(xié)議進(jìn)行通信時,通信開銷受到通信消息大小和通信頻率這兩方面的影響。消息越大、頻率越高,相應(yīng)的通信開銷也就越大。如果想要實現(xiàn)高效的通信,可以從這兩方面入手去調(diào)優(yōu)

3.1Gossip 消息大小

Redis 實例發(fā)送的 PING 消息的消息體是由 clusterMsgDataGossip 結(jié)構(gòu)體組成的,這個結(jié)構(gòu)體的定義如下所示:

typedef struct {
    char nodename[CLUSTER_NAMELEN];  //40字節(jié)
    uint32_t ping_sent; //4字節(jié)
    uint32_t pong_received; //4字節(jié)
    char ip[NET_IP_STR_LEN]; //46字節(jié)
    uint16_t port;  //2字節(jié)
    uint16_t cport;  //2字節(jié)
    uint16_t flags;  //2字節(jié)
    uint32_t notused1; //4字節(jié)
} clusterMsgDataGossip;

其中,CLUSTER_NAMELEN 和 NET_IP_STR_LEN 的值分別是 40 和 46,分別表示,nodename 和 ip 這兩個字節(jié)數(shù)組的長度是 40 字節(jié)和 46 字節(jié),我們再把結(jié)構(gòu)體中其它信息的大小加起來,就可以得到一個 Gossip 消息的大小了,即 104 字節(jié)。

每個實例在發(fā)送一個 Gossip 消息時,除了會傳遞自身的狀態(tài)信息,默認(rèn)還會傳遞集群十分之一實例的狀態(tài)信息。

例子:

所以,對于一個包含了 1000 個實例的集群來說,每個實例發(fā)送一個 PING 消息時,會包含 100 個實例的狀態(tài)信息,總的數(shù)據(jù)量是 10400 字節(jié),再加上發(fā)送實例自身的信息,一個 Gossip 消息大約是 10KB。為了讓 Slot 映射表能夠在不同實例間傳播,PING 消息中還帶有一個長度為 16,384 bit 的 Bitmap,這個 Bitmap 的每一位對應(yīng)了一個 Slot,如果某一位為 1,就表示這個 Slot 屬于當(dāng)前實例。這個 Bitmap 大小換算成字節(jié)后,是 2KB實例狀態(tài)信息和 Slot 分配信息相加,就可以得到一個 PING 消息的大小了,大約是 12KB。

PONG 消息和 PING 消息的內(nèi)容一樣,它的大小大約是 12KB。每個實例發(fā)送了 PING 消息后,還會收到返回的 PONG 消息,兩個消息加起來有 24KB。

從絕對值上來看,24KB 并不算很大,但是,如果實例正常處理的單個請求只有幾 KB 的話,那么,實例為了維護(hù)集群狀態(tài)一致傳輸?shù)?PING/PONG 消息,就要比單個業(yè)務(wù)請求大了。而且,每個實例都會給其它實例發(fā)送 PING/PONG 消息。隨著集群規(guī)模增加,這些心跳消息的數(shù)量也會越多,會占據(jù)一部分集群的網(wǎng)絡(luò)通信帶寬,進(jìn)而會降低集群服務(wù)正常客戶端請求的吞吐量。

3.2實例間通信頻率

Redis Cluster 的實例啟動后,默認(rèn)會每秒從本地的實例列表中隨機(jī)選出 5 個實例,再從這 5 個實例中找出一個最久沒有通信的實例,把 PING 消息發(fā)送給該實例。這是實例周期性發(fā)送 PING 消息的基本做法

這里有一個問題:實例選出來的這個最久沒有通信的實例,畢竟是從隨機(jī)選出的 5 個實例中挑選的,這并不能保證這個實例就一定是整個集群中最久沒有通信的實例。可能會出現(xiàn),有些實例一直沒有被發(fā)送 PING 消息,導(dǎo)致它們維護(hù)的集群狀態(tài)已經(jīng)過期。

為了避免這種情況,Redis Cluster 的實例會按照每 100ms 一次的頻率,掃描本地的實例列表,如果發(fā)現(xiàn)有實例最近一次接收 PONG 消息的時間,已經(jīng)大于配置項 cluster-node-timeout 的一半了(cluster-node-timeout/2),就會立刻給該實例發(fā)送 PING 消息,更新這個實例上的集群狀態(tài)信息

當(dāng)集群規(guī)模擴(kuò)大之后,因為網(wǎng)絡(luò)擁塞或是不同服務(wù)器間的流量競爭,會導(dǎo)致實例間的網(wǎng)絡(luò)通信延遲增加。如果有部分實例無法收到其它實例發(fā)送的 PONG 消息,就會引起實例之間頻繁地發(fā)送 PING 消息,這又會對集群網(wǎng)絡(luò)通信帶來額外的開銷

總結(jié)下單實例每秒會發(fā)送的 PING 消息數(shù)量,如下所示:

PING 消息發(fā)送數(shù)量 = 1 + 10 * 實例數(shù)(最近一次接收 PONG 消息的時間超出 cluster-node-timeout/2)

其中,1 是指單實例常規(guī)按照每 1 秒發(fā)送一個 PING 消息,10 是指每 1 秒內(nèi)實例會執(zhí)行 10 次檢查,每次檢查后會給 PONG 消息超時的實例發(fā)送消息。

例子:

假設(shè)單個實例檢測發(fā)現(xiàn),每 100 毫秒有 10 個實例的 PONG 消息接收超時,那么,這個實例每秒就會發(fā)送 101 個 PING 消息,約占 1.2MB/s 帶寬。如果集群中有 30 個實例按照這種頻率發(fā)送消息,就會占用 36MB/s 帶寬,這就會擠占集群中用于服務(wù)正常請求的帶寬

4.如何降低實例間的通信開銷?

4.1減小實例傳輸?shù)南⒋笮?/h4>

為了降低實例間的通信開銷,從原理上說,可以減小實例傳輸?shù)南⒋笮。≒ING/PONG 消息、Slot 分配信息),但是,因為集群實例依賴 PING、PONG 消息和 Slot 分配信息,來維持集群狀態(tài)的統(tǒng)一,一旦減小了傳遞的消息大小,就會導(dǎo)致實例間的通信信息減少,不利于集群維護(hù),所以,減小實例傳輸?shù)南⒋笮〔荒懿捎眠@種方式。

4.2降低實例間發(fā)送消息的頻率:

實例間發(fā)送消息的頻率有兩個。

  1. 每個實例每 1 秒發(fā)送一條 PING 消息。這個頻率不算高,如果再降低該頻率的話,集群中各實例的狀態(tài)可能就沒辦法及時傳播了。
  2. 每個實例每 100 毫秒會做一次檢測,給 PONG 消息接收超過 cluster-node-timeout/2 的節(jié)點發(fā)送 PING 消息。實例按照每 100 毫秒進(jìn)行檢測的頻率,是 Redis 實例默認(rèn)的周期性檢查任務(wù)的統(tǒng)一頻率,我們一般不需要修改它。

就只有 cluster-node-timeout 這個配置項可以修改

配置項 cluster-node-timeout 定義了集群實例被判斷為故障的心跳超時時間,默認(rèn)是 15 秒。如果 cluster-node-timeout 值比較小,那么,在大規(guī)模集群中,就會比較頻繁地出現(xiàn) PONG 消息接收超時的情況,從而導(dǎo)致實例每秒要執(zhí)行 10 次“給 PONG 消息超時的實例發(fā)送 PING 消息”這個操作

所以,為了避免過多的心跳消息擠占集群帶寬,可以調(diào)大 cluster-node-timeout 值,比如說調(diào)大到 20 秒或 25 秒。這樣一來, PONG 消息接收超時的情況就會有所緩解,單實例也不用頻繁地每秒執(zhí)行 10 次心跳發(fā)送操作了。

也不要把 cluster-node-timeout 調(diào)得太大,否則,如果實例真的發(fā)生了故障,需要等待 cluster-node-timeout 時長后,才能檢測出這個故障,這又會導(dǎo)致實際的故障恢復(fù)時間被延長,會影響到集群服務(wù)的正常使用

為了驗證調(diào)整 cluster-node-timeout 值后,是否能減少心跳消息占用的集群網(wǎng)絡(luò)帶寬,建議:可以在調(diào)整 cluster-node-timeout 值的前后,使用 tcpdump 命令抓取實例發(fā)送心跳信息網(wǎng)絡(luò)包的情況。

執(zhí)行下面的命令后,可以抓取到 192.168.10.3 機(jī)器上的實例從 16379 端口發(fā)送的心跳網(wǎng)絡(luò)包,并把網(wǎng)絡(luò)包的內(nèi)容保存到 r1.cap 文件中:

tcpdump host 192.168.10.3 port 16379 -i 網(wǎng)卡名 -w /tmp/r1.cap

通過分析網(wǎng)絡(luò)包的數(shù)量和大小,就可以判斷調(diào)整 cluster-node-timeout 值前后,心跳消息占用的帶寬情況了。

5.通信開銷總結(jié)

Redis Cluster 實例間以 Gossip 協(xié)議進(jìn)行通信的機(jī)制。Redis Cluster 運(yùn)行時,各實例間需要通過 PING、PONG 消息進(jìn)行信息交換,這些心跳消息包含了當(dāng)前實例和部分其它實例的狀態(tài)信息,以及 Slot 分配信息。這種通信機(jī)制有助于 Redis Cluster 中的所有實例都擁有完整的集群狀態(tài)信息。

但是,隨著集群規(guī)模的增加,實例間的通信量也會增加。如果盲目地對 Redis Cluster 進(jìn)行擴(kuò)容,就可能會遇到集群性能變慢的情況。這是因為,集群中大規(guī)模的實例間心跳消息會擠占集群處理正常請求的帶寬。而且,有些實例可能因為網(wǎng)絡(luò)擁塞導(dǎo)致無法及時收到 PONG 消息,每個實例在運(yùn)行時會周期性地(每秒 10 次)檢測是否有這種情況發(fā)生,一旦發(fā)生,就會立即給這些 PONG 消息超時的實例發(fā)送心跳消息。

集群規(guī)模越大,網(wǎng)絡(luò)擁塞的概率就越高,相應(yīng)的,PONG 消息超時的發(fā)生概率就越高,這就會導(dǎo)致集群中有大量的心跳消息,影響集群服務(wù)正常請求??梢酝ㄟ^調(diào)整 cluster-node-timeout 配置項減少心跳消息的占用帶寬情況,但是,在實際應(yīng)用中,如果不是特別需要大容量集群,建議把 Redis Cluster 的規(guī)模控制在 400~500 個實例。

設(shè)單個實例每秒能支撐 8 萬請求操作(8 萬 QPS),每個主實例配置 1 個從實例,那么,400~ 500 個實例可支持 1600 萬~2000 萬 QPS(200/250 個主實例 *8 萬 QPS=1600/2000 萬 QPS),這個吞吐量性能可以滿足不少業(yè)務(wù)應(yīng)用的需求

如果我們采用跟 Codis 保存 Slot 分配信息相類似的方法,把集群實例狀態(tài)信息和 Slot 分配信息保存在第三方的存儲系統(tǒng)上(例如 Zookeeper),這種方法會對集群規(guī)模產(chǎn)生什么影響嗎?

答案:假設(shè)我們將 Zookeeper 作為第三方存儲系統(tǒng),保存集群實例狀態(tài)信息和 Slot 分配信息,那么,實例只需要和 Zookeeper 通信交互信息,實例之間就不需要發(fā)送大量的心跳消息來同步集群狀態(tài)了。這種做法可以減少實例之間用于心跳的網(wǎng)絡(luò)通信量,有助于實現(xiàn)大規(guī)模集群。

而且,網(wǎng)絡(luò)帶寬可以集中用在服務(wù)客戶端請求上。不過,在這種情況下,實例獲取或更新集群狀態(tài)信息時,都需要和 Zookeeper 交互,Zookeeper 的網(wǎng)絡(luò)通信帶寬需求會增加。所以,采用這種方法的時候,需要給 Zookeeper 保證一定的網(wǎng)絡(luò)帶寬,避免 Zookeeper 受限于帶寬而無法和實例快速通信。

以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。 

相關(guān)文章

  • 控制Redis的hash的field中的過期時間

    控制Redis的hash的field中的過期時間

    這篇文章主要介紹了控制Redis的hash的field中的過期時間問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-01-01
  • Redisson如何解決redis分布式鎖過期時間到了業(yè)務(wù)沒執(zhí)行完問題

    Redisson如何解決redis分布式鎖過期時間到了業(yè)務(wù)沒執(zhí)行完問題

    這篇文章主要介紹了Redisson如何解決redis分布式鎖過期時間到了業(yè)務(wù)沒執(zhí)行完問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-01-01
  • Redis中Redisson布隆過濾器的學(xué)習(xí)

    Redis中Redisson布隆過濾器的學(xué)習(xí)

    布隆過濾器是一個非常長的二進(jìn)制向量和一系列隨機(jī)哈希函數(shù)的組合,可用于檢索一個元素是否存在,本文就詳細(xì)的介紹一下Redisson布隆過濾器,具有一定的參考價值,感興趣的可以了解一下
    2022-05-05
  • Redis 安裝 redistimeseries.so(時間序列數(shù)據(jù)類型)的配置步驟

    Redis 安裝 redistimeseries.so(時間序列數(shù)據(jù)類型)的配置步驟

    這篇文章主要介紹了Redis 安裝 redistimeseries.so(時間序列數(shù)據(jù)類型)詳細(xì)教程,配置步驟需要先下載redistimeseries.so 文件,文中介紹了啟動失敗問題排查,需要的朋友可以參考下
    2024-01-01
  • 詳解Redis集群搭建的三種方式

    詳解Redis集群搭建的三種方式

    Redis是一個開源的key-value存儲系統(tǒng),大部分互聯(lián)網(wǎng)企業(yè)都用來做服務(wù)器端緩存。Redis在3.0版本前只支持單實例模式,雖然支持主從模式、哨兵模式部署來解決單點故障,但是現(xiàn)在互聯(lián)網(wǎng)企業(yè)動輒大幾百G的數(shù)據(jù),沒法滿足業(yè)務(wù)的需求,所以Redis在3.0版本以后就推出了集群模式。
    2021-05-05
  • Redis獲取某個大key值的腳本實例

    Redis獲取某個大key值的腳本實例

    這篇文章主要給大家分享介紹了關(guān)于Redis獲取某個大key值的一個腳本實例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧。
    2018-04-04
  • Redis?數(shù)據(jù)恢復(fù)及持久化策略分析

    Redis?數(shù)據(jù)恢復(fù)及持久化策略分析

    本文將詳細(xì)分析Redis的數(shù)據(jù)恢復(fù)機(jī)制,持久化策略及其特點,并討論選擇持久化策略時需要考慮的因素,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2024-06-06
  • Redis全文搜索教程之創(chuàng)建索引并關(guān)聯(lián)源數(shù)據(jù)的教程

    Redis全文搜索教程之創(chuàng)建索引并關(guān)聯(lián)源數(shù)據(jù)的教程

    RediSearch提供了一種簡單快速的方法對 hash 或者 json 類型數(shù)據(jù)的任何字段建立二級索引,然后就可以對被索引的 hash 或者 json 類型數(shù)據(jù)字段進(jìn)行搜索和聚合操作,這篇文章主要介紹了Redis全文搜索教程之創(chuàng)建索引并關(guān)聯(lián)源數(shù)據(jù),需要的朋友可以參考下
    2023-12-12
  • 使用Redis存儲SpringBoot項目中Session的詳細(xì)步驟

    使用Redis存儲SpringBoot項目中Session的詳細(xì)步驟

    在開發(fā)Spring Boot項目時,我們通常會遇到如何高效管理Session的問題,默認(rèn)情況下,Spring Boot會將Session存儲在內(nèi)存中,今天,我們將學(xué)習(xí)如何將Session存儲從內(nèi)存切換到Redis,并驗證配置是否成功,需要的朋友可以參考下
    2024-06-06
  • Windows下安裝Redis服務(wù)的圖文教程

    Windows下安裝Redis服務(wù)的圖文教程

    Redis是有名的NoSql數(shù)據(jù)庫,一般Linux都會默認(rèn)支持。但在Windows環(huán)境中,可能需要手動安裝設(shè)置才能有效使用。下面通過本文給大家介紹Windows下安裝Redis服務(wù)的圖文教程,感興趣的朋友一起看看吧
    2018-08-08

最新評論