1. 程式人生 > >redis及叢集效能測試

redis及叢集效能測試

Redis叢集

文章彙總來源:由Drizzt0878大神提供

1      背景

這段時間專案需要對快取進行叢集,以下通過對redis叢集研究與測試,對比直接採用單機記憶體快取方式的效能。文章記錄本人學習與實踐redis叢集的一些步驟與測試。

2      目標

1. redis叢集研究與測試,分析其的可用性、可靠性、可運維性以及效能等方面。

2. 比較單機與叢集的效能,記憶體佔用等方面,為重構提供資料分析。

3.1    前言

為了提供程式的處理能力,通常會把熱點資料儲存在記憶體當中而不是直接從資料庫中讀取,從而減輕資料庫壓力與提高效能。Redis就是這樣這個優秀的快取中介軟體,由於它強大高效而又便捷的功能,得到了廣泛的使用。

Redis叢集三種常見的解決方案如下:

1、客戶端分片:這種方案將分片工作放在業務程式端,程式程式碼根據預先設定的路由規則,直接對多個Redis例項進行分散式訪問。這樣的好處是,不依賴於第三方分散式中介軟體,實現方法和程式碼都自己掌控,可隨時調整,不用擔心踩到坑。這實際上是一種靜態分片技術。Redis例項的增減,都得手工調整分片程式。基於此分片機制的開源產品,現在仍不多見。這種分片機制的效能比代理式更好(少了一箇中間分發環節)。但缺點是升級麻煩,對研發人員的個人依賴性強——需要有較強的程式開發能力做後盾。如果主力程式設計師離職,可能新的負責人,會選擇重寫一遍。所以,這種方式下,可運維性較差。出現故障,定位和解決都得研發和運維配合著解決,故障時間變長。因此這種方案,難以進行標準化運維,不太適合中小公司(除非有足夠的DevOPS)。

2、代理分片:這種方案,將分片工作交給專門的代理程式來做。代理程式接收到來自業務程式的資料請求,根據路由規則,將這些請求分發給正確的Redis例項並返回給業務程式。這種機制下,一般會選用第三方代理程式(而不是自己研發),因為後端有多個Redis例項,所以這類程式又稱為分散式中介軟體。這樣的好處是,業務程式不用關心後端Redis例項,運維起來也方便。雖然會因此帶來些效能損耗,但對於Redis這種記憶體讀寫型應用,相對而言是能容忍的。這是我們推薦的叢集實現方案。像基於該機制的開源產品Twemproxy,Codis便是其中代表,應用非常廣泛。

3、伺服器端分片:建立在基於無中心分散式架構之上(沒有代理節點效能瓶頸問題)。Redis-Cluster即為官方基於該架構的解決方案。RedisCluster將所有Key對映到16384個Slot中,叢集中每個Redis例項負責一部分,業務程式通過整合的RedisCluster客戶端進行操作。客戶端可以向任一例項發出請求,如果所需資料不在該例項中,則該例項引導客戶端自動去對應例項讀寫資料。Redis Cluster的成員管理(節點名稱、IP、埠、狀態、角色)等,都通過節點之間兩兩通訊,定期交換並更新。

中介軟體名稱

特點

Twemproxy

Twemproxy是一種代理分片機制,由Twitter開源。Twemproxy作為代理,可接受來自多個程式的訪問,按照路由規則,轉發給後臺的各個Redis伺服器,再原路返回。這個方案順理成章地解決了單個Redis例項承載能力的問題。當然,Twemproxy本身也是單點,需要用Keepalived做高可用方案。這麼些年來,Twemproxy是應用範圍最廣、穩定性最高、最久經考驗的分散式中介軟體。只是,他還有諸多不方便之處。Twemproxy最大的痛點在於,無法平滑地擴容/縮容。這樣增加了運維難度:業務量突增,需增加Redis伺服器;業務量萎縮,需要減少Redis伺服器。但對Twemproxy而言,基本上都很難操作。或者說,Twemproxy更加像伺服器端靜態sharding。有時為了規避業務量突增導致的擴容需求,甚至被迫新開一個基於Twemproxy的Redis叢集。Twemproxy另一個痛點是,運維不友好,甚至沒有控制面板。當然,由於使用了中介軟體代理,相比客戶端直接連伺服器方式,效能上有所損耗,實測結果降低了20%左右。

Codis

Codis由豌豆莢於2014年11月開源,基於Go和C開發,是近期湧現的、國人開發的優秀開源軟體之一。現已廣泛用於豌豆莢的各種Redis業務場景,從各種壓力測試來看,穩定性符合高效運維的要求。效能更是改善很多,最初比Twemproxy慢20%;現在比Twemproxy快近100%(條件:多例項,一般Value長度)。Codis具有視覺化運維管理介面。Codis無疑是為解決Twemproxy缺點而出的新解決方案。因此綜合方面會由於Twemproxy很多。目前也越來越多公司選擇Codis。Codis引入了Group的概念,每個Group包括1個RedisMaster及至少1個Redis Slave,這是和Twemproxy的區別之一。這樣做的好處是,如果當前Master有問題,則運維人員可通過Dashboard“自助式”切換到Slave,而不需要小心翼翼地修改程式配置檔案。為支援資料熱遷移(Auto Rebalance),出品方修改了Redis Server原始碼,並稱之為Codis Server。Codis採用預先分片(Pre-Sharding)機制,事先規定好了,分成1024個slots(也就是說,最多能支援後端1024個Codis Server),這些路由資訊儲存在ZooKeeper中。不足之處有對redis原始碼進行了修改,以及代理實現本身會有的問題。

Redis-cluster

reids-cluster在redis3.0中推出,支援Redis分散式叢集部署模式。採用無中心分散式架構。所有的redis節點彼此互聯(PING-PONG機制),內部使用二進位制協議優化傳輸速度和頻寬.節點的fail是通過叢集中超過半數的節點檢測失效時才生效.客戶端與redis節點直連,不需要中間proxy層.客戶端不需要連線叢集所有節點,連線叢集中任何一個可用節點即可,減少了代理層,大大提高了效能。redis-cluster把所有的物理節點對映到[0-16383]slot上,cluster 負責維護node<->slot<->key之間的關係。目前Jedis已經支援Redis-cluster。從計算架構或者效能方面無疑Redis-cluster是最佳的選擇方案。(PS:雖然Redis-cluster從方案選型上面比較佔據優勢,而且官方推出後各方相應挺熱,但是由於Redis-cluster剛推出不久,雖然官方宣傳已經發布的是文件版本,但穩定性方面還有待驗證)。

根據參考官方的更新來看,RedisClister不斷趨向於普及與完善,社群的支援程度較熱,本文以下主要採用Redis Clister實現叢集,並進行測試。

Redis3.0 最大的特點就是有了cluster的能力,使用redis-trib.rb工具可以輕鬆構建Redis Cluster。Redis Cluster採用無中心結構,每個節點儲存資料和整個叢集狀態,每個節點都和其他所有節點連線。節點之間使用gossip協議傳播資訊以及發現新節點,這種結構和Cassandra很相似,Cassandra節點可以轉發請求。Redis叢集中節點不作為client請求的代理,client根據node返回的錯誤資訊重定向請求。rediscluster在設計的時候,就考慮到了去中心化,去中介軟體,也就是說,叢集中的每個節點都是平等的關係,都是對等的,每個節點都儲存各自的資料和整個叢集的狀態。每個節點都和其他所有節點連線,而且這些連線保持活躍,這樣就保證了我們只需要連線叢集中的任意一個節點,就可以獲取到其他節點的資料。

Redis 叢集是一個提供在多個Redis間節點間共享資料的程式集。

Redis叢集並不支援處理多個keys的命令,因為這需要在不同的節點間移動資料,從而達不到像Redis那樣的效能,在高負載的情況下可能會導致不可預料的錯誤。

Redis 叢集通過分割槽來提供一定程度的可用性,在實際環境中當某個節點宕機或者不可達的情況下繼續處理命令. Redis 叢集的優勢:

自動分割資料到不同的節點上。

整個叢集的部分節點失敗或者不可達的情況下能夠繼續處理命令。

那麼redis 是如何合理分配這些節點和資料的呢?

Redis 叢集沒有並使用傳統的一致性雜湊來分配資料,而是採用另外一種叫做雜湊槽 (hash slot)的方式來分配的。rediscluster 預設分配了 16384 個slot,當我們set一個key 時,會用CRC16演算法來取模得到所屬的slot,然後將這個key 分到雜湊槽區間的節點上,具體演算法就是:CRC16(key) % 16384。

假設現在有3個節點已經組成了叢集,分別是:A, B, C 三個節點,它們可以是一臺機器上的三個埠,也可以是三臺不同的伺服器。那麼,採用雜湊槽 (hash slot)的方式來分配16384個slot 的話,它們三個節點分別承擔的slot 區間是:

l  節點A覆蓋0-5460;

l  節點B覆蓋5461-10922;

l  節點C覆蓋10923-16383.

這種雜湊槽的分配方式有好也有壞,好處就是很清晰,比如我想新增一個節點D,redis cluster的這種做法是從各個節點的前面各拿取一部分slot到D上,我會在接下來的實踐中實驗。大致就會變成這樣:

l  節點A覆蓋1365-5460

l  節點B覆蓋6827-10922

l  節點C覆蓋12288-16383

l  節點D覆蓋0-1364,5461-6826,10923-12287

同樣刪除一個節點也是類似,移動完成後就可以刪除這個節點了。

所以redis cluster 就是這樣的一個形狀:

為了使在部分節點失敗或者大部分節點無法通訊的情況下叢集仍然可用,所以叢集使用了主從複製模型,每個節點都會有N-1個複製品。

在我們例子中具有A,B,C三個節點的叢集,在沒有複製模型的情況下,如果節點B失敗了,那麼整個叢集就會以為缺少5501-11000這個範圍的槽而不可用。

然而如果在叢集建立的時候(或者過一段時間)我們為每個節點新增一個從節點A1,B1,C1,那麼整個叢集便有三個master節點和三個slave節點組成,這樣在節點B失敗後,叢集便會選舉B1為新的主節點繼續服務,整個叢集便不會因為槽找不到而不可用了。

不過當B和B1 都失敗後,叢集就不可用了。

redis-cluster 選舉:


1.      領著選舉過程是叢集中所有master參與,如果半數以上master節點與master

節點通訊超過(cluster-node-timeout),認為當前master節點掛掉。

2.      什麼時候整個叢集不可用(cluster_state:fail),當叢集不可用時,所有對叢集的操作做都不可用,收到((error) CLUSTERDOWN The cluster is down)錯誤

a: 如果叢集任意master掛掉,且當前master沒有slave。叢集進入fail狀態,也

可以理解成進群的slot對映[0-16383]不完成時進入fail狀態。

b: 如果進群超過半數以上master掛掉,無論是否有slave叢集進入fail狀態。

Redis Cluster不提供強一致性。

例如cluster接受了一個寫請求,給client返回ok,這個寫請求的內容也可能丟失。因為其寫流程如下:

1. master B接受了一個寫請求;

2. B寫成功,返回ok給client;

3. B把資料廣播給slaves(B1、B2、B3)

如果第二步執行完畢後,B crash了,則會發生資料不一致現象。這與傳統的DBMS類似,它們接收了寫請求後,每隔1S才會把資料寫入disk,這麼做也是在效能和一致性之間做一個平衡。(如果使用者對資料的一致性要求比較高,Redis日後可能也會兼顧這種需求,將來會提供相應的選項,讓redis中的slave沒用成功的接受資料之前不會給client返回ok給client。即先執行step 3,然後再執行step 2。)

一致性還有一種場景。假設有client Z,與cluster內各個node A and B and C,以及各個node的replica A1 and B1 and C1,Z與B之間連線正常,但是B與B1以及cluster內其他nodes連線失敗。如果Z發起write request,那麼B會給他返回ok,但是B1無法獲取到相應的資料,這就要求寫的時候也要把node與cluster內其他的成員的探活也要考慮在內。基本要求就是,寫時間週期要大於探活時間週期(node timeout)。當node Btimeout之後,masterB會自動進入failing狀態,拒絕外部client的連線請求,而cluster則會選出slave B1來代替B。

cluster-enabled<yes/no>: 是否開啟叢集

cluster-config-file<filename>:,Redis執行時儲存配置的檔案,自動生成,所以不能修改這個檔案。

cluster-node-timeout<milliseconds>: 配置結點超時多久則認為它失效了。如果是主節點超時被認為失效,將由其從節點通過故障轉移成為主節點。

cluster-slave-validity-factor<factor>: 失效校驗次數。連線一個節點超時超過此配置的次數,才認為失效。例如配置為5,timeout為5秒,則從節點與主節點超時50秒後才會嘗試故障轉移。

 cluster-migration-barrier <count>: 主節點至少從節點數。配置該引數,當另一個主節點沒有了從節點覆蓋,將對部分從節點進行遷移到該主節點。

cluster-require-full-coverage<yes/no>: 預設為yes,只要有結點宕機導致16384個槽沒全被覆蓋,整個叢集就全部停止接受寫入操作。如果設定為no,則仍然可以接收部分請求(仍被覆蓋的槽對應keys子集)。

叢集  :

CLUSTER INFO 列印叢集的資訊 

CLUSTER NODES 列出叢集當前已知的所有節點(node),以及這些節點的相關資訊。 

節點  :

CLUSTER MEET <ip> <port> 將 ip 和 port 所指定的節點新增到叢集當中,讓它成為叢集的一份子。 

CLUSTER FORGET <node_id> 從叢集中移除 node_id 指定的節點。 

CLUSTER REPLICATE <node_id> 將當前節點設定為 node_id 指定的節點的從節點。 

CLUSTER SAVECONFIG 將節點的配置檔案儲存到硬盤裡面。 

槽(slot)  :

CLUSTER ADDSLOTS <slot> [slot ...] 將一個或多個槽(slot)指派(assign)給當前節點。 

CLUSTER DELSLOTS <slot> [slot ...] 移除一個或多個槽對當前節點的指派。 

CLUSTER FLUSHSLOTS 移除指派給當前節點的所有槽,讓當前節點變成一個沒有指派任何槽的節點。 

CLUSTER SETSLOT <slot> NODE <node_id> 將槽 slot 指派給 node_id 指定的節點,如果槽已經指派給另一個節點,那麼先讓另一個節點刪除該槽>,然後再進行指派。 

CLUSTER SETSLOT <slot> MIGRATING <node_id> 將本節點的槽 slot 遷移到 node_id 指定的節點中。 

CLUSTER SETSLOT <slot> IMPORTING <node_id> 從 node_id 指定的節點中匯入槽 slot 到本節點。 

CLUSTER SETSLOT <slot> STABLE 取消對槽 slot 的匯入(import)或者遷移(migrate)。 

鍵  :

CLUSTER KEYSLOT <key> 計算鍵 key 應該被放置在哪個槽上。 

CLUSTER COUNTKEYSINSLOT <slot> 返回槽 slot 目前包含的鍵值對數量。 

CLUSTER GETKEYSINSLOT <slot> <count> 返回 count 個 slot 槽中的鍵。 

這些命令是叢集所獨有的。執行上述命令要先登入:

redis-cli -c -p6382 -h 172.16.21.139    //登入

搭建叢集的第一件事情我們需要一些執行在叢集模式的Redis例項。這意味這叢集並不是由一些普通的Redis例項組成的,叢集模式需要通過配置啟用,開啟叢集模式後的Redis例項便可以使用叢集特有的命令和特性了。

1. 環境

要讓叢集正常工作至少需要3個主節點,如果不足,建立時會有以下提示:

在這裡我們要建立6個redis節點,其中三個為主節點,三個為從節點,對應的redis節點的ip和埠對應關係如下:

127.0.0.1:7000

127.0.0.1:7001

127.0.0.1:7002

127.0.0.1:7003

127.0.0.1:7004

127.0.0.1:7005

2. 下載編譯

wget http://download.redis.io/releases/redis-3.0.7.tar.gz

tar -zxvf redis-3.0.7.tar.gz

make

3. 建立叢集需要的目錄

mkdir /usr/local/redis-3.0.7/cluster

cd /usr/local/redis-3.0.7/cluster

mkdir 7000

mkdir 7001

mkdir 7002

mkdir 7003

mkdir 7004

mkdir 7005

4. 配置檔案redis.conf

下面是一個最少選項的叢集的配置檔案,先拷貝一份作為作修改:

cp /usr/local/redis-3.0.7/redis.conf /usr/local/redis-3.0.7/cluster/

vi redis.conf

port 7000

daemonize yes

cluster-enabled yes

cluster-config-file nodes.conf

cluster-node-timeout 5000

appendonly yes

##修改完 redis.conf 配置檔案中的這些配置項之後把這個配置檔案分別拷貝到

7000/7001/7002/7003/7004/7005目錄下面:

cp /usr/local/redis-3.0.7/cluster/redis.conf /usr/local/redis-3.0.7/cluster/7000

cp /usr/local/redis-3.0.7/cluster/redis.conf /usr/local/redis-3.0.7/cluster/7001

cp /usr/local/redis-3.0.7/cluster/redis.conf /usr/local/redis-3.0.7//cluster/7002

cp /usr/local/redis-3.0.7/cluster/redis.conf /usr/local/redis-3.0.7/cluster/7003

cp /usr/local/redis-3.0.7/cluster/redis.conf /usr/local/redis-3.0.7/cluster/7004

cp /usr/local/redis-3.0.7/cluster/redis.conf /usr/local/redis-3.0.7/cluster/7005

每個例項配置類似,修改一下都應埠、IP地址、檔名稱即可。

Redis叢集由多個執行在叢集模式(cluster mode)下的Redis例項組成,例項的叢集模式需要通過配置來開啟,開啟叢集模式的例項將可以使用叢集特有的功能和命令。要讓叢集正常運作至少需要三個主節點, 不過在剛開始試用叢集功能時,強烈建議使用六個節點:其中三個為主節點,而其餘三個則是各個主節點的從節點。

常用配置資訊:

繫結地址:bind 192.168.XXX.XXX。不能繫結到127.0.0.1或localhost,否則指導客戶端重定向時會報”Connection refused”的錯誤。

後臺執行:daemonize yes

輸出日誌:logfile “./redis-7000.log”

監聽埠:port 7000

Cluster:cluster-enabled yes

cluster-config-file nodes-7000.conf

cluster-node-timeout 15000

cluster-require-full-coverage yes

5. 啟動redis例項

cd /usr/local/redis-3.0.7/

./src/redis-server cluster/7000/redis.conf

./src/redis-server cluster/7001/redis.conf

例項列印的日誌顯示, 因為 nodes.conf 檔案不存在,所以每個節點都為它自身指定了一個新的 ID :

[82462]26 Nov 11:56:55.329 * No cluster configuration found, I'm97a3a64667477371c4479320d683e4c8db5858b1

啟動之後使用命令檢視redis的啟動情況 ps –ef | grep redis

6. 執行redis的建立叢集命令建立叢集

使用redis-trib.rb建立叢集:

src/redis-trib.rb create --replicas 1 127.0.0.1:7000 127.0.0.1:7001 127.0.0.1:7002 127.0.0.1:7003 127.0.0.1:7004 127.0.0.1:7005

# redis-trib.rb 的create子命令構建

# --replicas 則指定了為Redis Cluster中的每個Master節點配備幾個Slave節點

# 節點角色由順序決定,先master之後是slave

如果執行上述命令報錯的時候會報錯,因為是執行的ruby的指令碼,需要ruby的環境,錯誤內容:/usr/bin/env: ruby: No such file or directory

所以需要安裝ruby的環境,這裡推薦使用yuminstall ruby安裝

yum install ruby

然後再執行建立叢集命令,還會報錯,提示不能載入redis,是因為缺少redis和ruby的介面,

錯誤內容:

/usr/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:31:in`gem_original_require': no such file to load -- redis (LoadError)

from/usr/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:31:in `require'

from./redis-trib.rb:25

使用yum安裝:

再執行建立叢集命令成功後會展示:

輸入yes,然後配置完成。

[OK] All16384 slots covered

這表示叢集中的16384個槽都有至少一個主節點在處理,叢集運作正常。redis叢集即搭建成功!

備註:在utils/create-cluster下提供了一個create-cluster指令碼,能夠創建出一個叢集,類似我們上面建立起的3主3從的叢集。

7.檢查叢集狀態

5      測試

   測試場景,分為幾種場景測試,分別對單節點單redis例項方式與兩臺主機多個redis例項分別插入1千萬條資料,查詢記錄等操作,進行效能分析,記憶體對比。以及對叢集的可用性,分別對某個節點進行停止服務,驗證其叢集的可用性。上述測試都是基於記憶體方式進行測試。

1、硬體配置:

CPU:Intel(R) Core(TM) i5-4590 CPU @ 3.30GHz * 2(2核處理器)

記憶體:4G

磁碟:75G

2、軟體配置:

作業系統:CentOSLinux release 7.1.1503(Linux version3.10.0-229.14.1.el7.x86_64)

Redis:3.0.7

JDK: 1.7.0_79

Jedis: 2.8.1

5.3    測試單機單redis例項

測試場景:使用單機啟動redis例項,插入1千萬條記錄(固定的key值)。

         測試步驟:

1.      修改redis.conf,以全記憶體方式執行:

快照rdb儲存方式部分全註釋了,

#save 900 1

#save 300 10

#save 60 10000

使用aof 持久化方式設定為no

appendfsync no

2.      啟動redis例項。

3 . 使用Jedis客戶端寫入1千萬條記錄(sadd方法),固定的key值,value值模擬規則生成(jedis.sadd("zimpes",s)),得出測試資料。

         備註:這裡插入的記錄有是有一定的業務邏輯,所以時間上會有所損耗。因此耗時並不是直接插入redis的時間。

 

 

測試結果

新增記錄1千萬

測試次數

第一次

第二次

第三次

耗時

1055.999(s)

1102.512(s)

1075.976(s)

記憶體消耗

955.70(M)

955.70(M)

955.70(M)

測試場景:在測試場景1的基礎上,執行查詢記錄1千萬次,查詢的值隨機。

         測試步驟:

1.      同1,以全記憶體方式啟動redis例項。

2 . 使用Jedis客戶端執行查詢記錄1千萬次(sismember方法),查詢值隨機生成,得出測試資料。

測試結果

記錄1千萬,查詢次數1千萬次

測試次數

第一次

第二次

第三次

耗時

1207.424(s)

1075.278(s)

1024.245(s)

測試場景:使用單機啟動redis例項,插入1千萬條記錄(模擬隨機生成key值)。

         測試步驟跟測試場景1相同:

1 . 使用Jedis客戶端寫入1千萬條記錄(sadd方法),模擬隨機生成key與value值模(jedis.sadd(s, s)),得出測試資料。

 

測試結果

新增記錄1千萬

測試次數

第一次

第二次

第三次

耗時

1365.425(s)

1366.234(s)

1427.347(s)

記憶體消耗

1044.34(M)

1044.30(M)

1044.30(M)

測試場景:在測試場景3的基礎上,執行查詢記錄1千萬次,查詢的值隨機。

         測試步驟:

1.      同3,以全記憶體方式啟動redis例項。

2 . 使用Jedis客戶端執行查詢記錄1千萬次(sismember方法),查詢值隨機生成,得出測試資料。

測試結果

記錄1千萬,查詢次數1千萬次

測試次數

第一次

第二次

第三次

耗時

 1452.37(s)

1287.173(s)

1408.166(s)

測試場景:使用兩臺主機,同上述配置,插入1千萬條記錄。

測試步驟:

1.      分別在兩臺機器上各啟動3個redis例項,分別為:

172.16.21.139:

172.16.21.131:

2.      執行../src/redis-trib.rbcreate --replicas 1 172.16.21.139:7000 172.16.21.139:7001 172.16.21.139:7002172.16.21.131:7003 172.16.21.131:7004 172.16.21.131:7005,啟動叢集:

從啟動日誌可以看出,三個主節點為:

M: 8092602c5491fad21ab71670a3c178f72964b2ba172.16.21.139:7000

M: e27291b9e2449c857373d4fd60f7b07f37886eed172.16.21.139:7001

M: 22d5c7cce58d559839c2914c34846afc23170320172.16.21.131:7003

三個從節點為:

S: 576e6ea7e133510dfa5e90d086e1330846f6fdd7172.16.21.139:7002

S: e46fc51206f05160dff40a7a585908bd55e9e9b5172.16.21.131:7004

S: 356a53b3146e0f8918d3530eba3d463f94397350172.16.21.131:7005

3 . 使用Jedis客戶端寫入1千萬條記錄(sadd方法),測試發現如果採用相同的key值時,只儲存到同一個節點,這樣測試並沒有多大意義,因此這裡測試採用的key與value使用的都是一樣,都是模擬隨機生成的,從而得出測試資料。

主機一

 

主機二

 

 

測試結果

新增記錄1千萬

測試次數

第一次

第二次

第三次

耗時

1014.444(s)

952.875(s)

962.408(s)

記憶體消耗

(主機1)

661.45+661.52+660.9

=1983.87(M)

661.51+661.86+660.41

=1983.78(M)

660.8+660.68+660.29

=1981.77(M)

記憶體消耗

(主機2)

661.9+660.47+660.5

=1982.87(M)

661.4+660.49+660.82

=1982.71(M)

661.31+661.77+661.72

=1984.8(M)

記憶體消耗

(總)

3966.49(M)

3966.57(M)

測試場景:在測試場景1的基礎上,執行查詢記錄1千萬次,查詢的值隨機。

         測試步驟:

1.      同1,以全記憶體方式啟動redis例項。

2 . 使用Jedis客戶端執行查詢記錄1千萬次(sismember方法),查詢值隨機生成,得出測試資料。

測試結果

主機一:

主機二:

記錄1千萬,查詢次數1千萬次

測試次數

第一次

第二次

第三次

耗時

1910.489(s)

1971.054(s)

1942.552(s)

測試場景:使用兩臺主機,同上述配置,起分別6個redis例項並對其進行叢集配置,分別測試新增新節點,Resharding,故障轉移,移除節點等實驗。

實驗主要通過redis-trib.rb命令進行操作,可以查詢到相關命令資訊:

[[email protected] src]# ./redis-trib.rb help

5.4.3.1  新增節點

1.      參考之前章節4搭建並使用Redis叢集,在已經啟動6個節點叢集的基礎上,在139機器上建立7010資料夾和相應修改埠為7010的redis.conf檔案;在131機器上建立7020資料夾和相應修改埠為7020的redis.conf檔案。並分別啟動兩個Redis例項:

[[email protected] cluster]# ../src/redis-server ./7010/redis.conf

[[email protected] cluster]# ../src/redis-server ./7020/redis.conf

當前的叢集資訊:

2.      使用程式模擬隨機生成記錄,保持到redis,資料分佈如下:

3.      使用redis-trib.rb add-node分別將兩個新結點新增到叢集中,一個作為Master,一個作為其Slave。

新增Master節點

[[email protected] src]# ./redis-trib.rb add-node 172.16.21.139:7010 172.16.21.139:7000

新新增的7010節點已經作為master節點,沒有包含任何資料, 因為它沒有包含任何 slot。,當叢集需要將某個從節點升級為新的主節點時,這個新節點不會被選中。

新增Slaver節點

./redis-trib.rb add-node --slave --master-id 6dc6b0be6f1eb4b0316c25856ea2a08361f2cf05 172.16.21.131:7020 172.16.21.139:7000

注意:線上新增slaver時,需要dump整個master程序,並傳遞到slaver,再由slaver

載入 rdb 檔案到記憶體,rdb 傳輸過程中Master可能無法提供服務,整個過程消耗大量 io,小心操作。

5.4.3.2  Resharding

新新增的節點仍不能提供服務,需要為其分配slot後才行。通過redis-trib.rbreshard可以互動式地遷移Slot。下面的例子將4000個Slot從其他master節點遷移到7010節點上。也可以通過./redis-trib.rb reshard <host>:<port> --from<node-id> --to <node-id> --slots --yes在程式中自動完成遷移。

1.      reshard:

./redis-trib.rb reshard 172.16.21.139:7000

途中會進行詢問:

需要遷移的slot數量

選擇要接受這些遷移slot的node-id

選擇slot來源,all表示從所有的master重新分配,或者資料要提取 slot 的 master 節點 id,最後用 done 結束

列印被移動的 slot 後,輸入 yes 開始移動 slot 以及對應的資料

此時7010已成功分配4000個slot:

2.      遷移完成後,檢視之前儲存的測試記錄的分佈情況,可以看到部分Key已經遷移到了新的結點7010上。

5.4.3.3  故障轉移

停掉主節點

Redis Cluster重用了Sentinel的程式碼邏輯,不需要單獨啟動一個Sentinel叢集,RedisCluster本身就能自動進行Master選舉和Failover切換。

1.      先檢視7010目前儲存的資料記錄:

2.      停掉7010節點,之後可以看到結點狀態變成了fail,而Slave 7011被選舉為新的Master。

3.      再次查詢之前儲存在7010節點上的資料,可以看到7020節點頂替了7010節點成為主節點後繼續提供服務,整個叢集沒有受到影響。

4.      重新啟動7010,可以發現自動重新恢復到叢集中。

5.      然後嘗試用程式不斷寫入記錄,中途停止7020節點。

此時自動切換7010為master節點:

程式仍可以進行寫入,表示叢集故障轉移是成功的。

重啟7020節點的例項,連線上去檢視現在資料量與7010節點上的資料量,發現是一致的,表示資料主從同步成功。

注意:這裡使用的是Jedis,初始化JedisCluster時有幾個引數需要重點關注的:

connectionTimeout

maxRedirections 嘗試重定向次數

以上connectionTimeout與soTimeout需要大於redis配置failover檢測主節點,最終從節點成功替代原主節點的時間段。不然會丟擲CLUSTER DOWN異常。

另外maxRedirections嘗試重定向次數將包含在上面兩個配置的時間段內,如果插入條數速度比較快此值需要設定比較大,不然在從節點生效之前,已經到達最大重定向次數的話,將會丟擲Too many Cluster redirections?異常。

5.4.3.4  刪除節點

1.      刪除節點前,查詢當前叢集資訊:

2.      分別進行刪除從節點與刪除主節點測試

刪除從節點

./redis-trib.rb del-node 172.16.21.131:7020 fe79dcbac250b97ec9df4102983fd2901f4823c6

執行完成後檢視叢集狀態與7020節點例項,發現都已經沒有了:

刪除主節點

使用同樣的方法移除主節點,不過在移除主節點前,需要確保這個主節點是空的。如果不是空的,需要將這個節點的資料重新分片到其他主節點上。

將7010節點的slot(4000)遷到7000上:

./redis-trib.rb reshard 172.16.21.139:7000

輸入要遷移的slot數量,這裡是4000

輸入要接受這些slot的node-id,這裡是7000節點的node-id

輸入被分配的源節點node-id,這裡需要被刪除7010節點的node-id

輸入done之後可以看到後臺輸出moving的資訊:

完成後可以查詢到7010節點的slots遷移到7000中了:

最後就可以執行刪除主節點操作

./redis-trib.rb del-node 172.16.21.139:7010 6dc6b0be6f1eb4b0316c25856ea2a08361f2cf05

執行完成後檢視叢集狀態與7020節點例項,發現都已經沒有了:

5.4.3.5  遷移從節點

1.      在特定的場景下,不需要系統管理員的協助下,自動將一個從節點從當前的主節點切換到另一個主節的自動重新配置的過程叫做複製遷移(從節點遷移),從節點的遷移能夠提升整個Redis叢集的可用性:

概況一下從節點遷移

叢集會在有從節點數量最多的主節點上進行從節點的遷移

要在一個主節點上新增多個從節點

引數來控制從節點遷移replica-migration-barrier:你可以仔細閱讀redis.conf。

2.      先查詢當前叢集的資訊:

3.      啟動7020節點例項,並新增到叢集中作為7000的從節點:

./redis-trib.rb add-node --slave --master-id d3e9e5fac92b43aee69f3f29d4679842ee7e979b 172.16.21.131:7020 172.16.21.139:7000

4.      新增成功後可以看到,7020已作為7000的從節點,此時7000的從節點有7004和7020兩個:

5.      7003只有一個從節點7002,嘗試刪除7002從節點:

./redis-trib.rb del-node 172.16.21.139:7002 559dee1a662ecdc508efcf402c4f19294e3b42dd

6.      刪除後已經沒有了7002例項,與叢集中7003已沒有從節點:

7.      使用replicate轉移7020從節點到7003主節點,成功後如下:

5.4.3.6  手動故障轉移

有的時候在主節點沒有任何問題的情況下強制手動故障轉移也是很有必要的,比如想要升級主節點的Redis程序,我們可以通過故障轉移將其轉為slave再進行升級操作來避免對叢集的可用性造成很大的影響。

Redis叢集使用 CLUSTER FAILOVER命令來進行故障轉移,不過要被轉移的主節點的從節點上執行該命令 手動故障轉移比主節點失敗自動故障轉移更加安全,因為手動故障轉移時客戶端的切換是在確保新的主節點完全複製了失敗的舊的主節點資料的前提下下發生的,所以避免了資料的丟失。

執行手動故障轉移時從節點日誌如下:

# Manual failover user request accepted.

# Received replication offset for pausedmaster manual failover: 347540

# All master replication stream processed,manual failover can start.

# Start of election delayed for 0milliseconds (rank #0, offset 347540).

# Starting a failover election for epoch7545.

# Failover election won: I'm the newmaster.

其基本過程如下:客戶端不再連結我們淘汰的主節點,同時主節點向從節點發送複製偏移量,從節點得到複製偏移量後故障轉移開始,接著通知主節點進行配置切換,當客戶端在舊的master上解鎖後重新連線到新的主節點上。

例如當前7002是7003的從節點,通過登入7002執行failover,主動實現故障遷移:

執行後,可以看到7002已經成為主節點,7003成為其從節點:

1.      單機上儲存1千萬條記錄(當前生成規則:key固定值與value模擬生成規則值)佔用記憶體大小約為955M,比較穩定,因此可以估計到1億條記錄大約至少需要9.4G記憶體。

2.      單機上儲存1千萬條記錄(當前生成規則:key與value都是模擬生成規則值)佔用記憶體大小約為1044.30M,比較穩定,因此可以估計到1億條記錄大約至少需要10.2G記憶體。插入資料的效能比第一種情況下降15%-28%,查詢的效能比第一種情況下降15%-25%。

3.      叢集的情況下儲存1千萬條記錄(當前生成規則:key與value都是模擬生成規則值),平均每臺機佔用總記憶體大小約為1984M,共約3966.6M,因此可以估計到1億條記錄大約至少共需要38.7G記憶體。

4.      同樣的新增記錄條件下(當前生成規則:key與value都是模擬生成規則值),新增記錄1千萬條,單機約為1400s,叢集約為977s,叢集效能提升大約30%。

5.      同樣的查詢記錄條件下(當前生成規則:key與value都是模擬生成規則值),存量記錄1千萬條,查詢1千萬次,單機約為1382s,叢集約為1941s,叢集效能損耗大約29%。

6.      Redis cluster採用Master-Slaver模式,自動檢測Master節點是否停掉,進行切換,整個叢集的部分節點失敗或者不可達的情況下能夠繼續處理命令,滿足高可用性。

7.      Redis cluster提供新增節點,刪除節點,遷移從節點等特性,通過提供的命令很方便地對叢集進行水平伸縮。

本文章了採用以下這些巨人的文章,非常感謝他們的分享:

http://ihenu.iteye.com/blog/2267881

http://blog.csdn.net/zhu_tianwei/article/details/44928779