1. Redis集群
1.1 概述
Redis3.0以后推出Redis cluster集群方案素标。
1.2 Reids cluster 架構(gòu)圖
架構(gòu)細節(jié)
- 所有reids節(jié)點彼此互聯(lián)(PING-PONG機制)报破,內(nèi)部使用二進制協(xié)議傳輸和帶寬。
- 節(jié)點的fail是通過集群中超過半數(shù)的節(jié)點檢測失效時才生效。
- 客戶端與redis節(jié)點直連耸彪,不需要中間件proxy隘世,客戶端不需要連接集群所有節(jié)點,連接集群中任何一個可用節(jié)點即可怯邪。
- reids-cluster把所有的物理節(jié)點映射到[0-16383]slot上绊寻,cluster負責維護node<->slot<->value
- 集群的整個數(shù)據(jù)庫被分為了16384個槽(slot),數(shù)據(jù)庫的每個鍵都屬于這16384槽中的一個悬秉,集群中的每個節(jié)點可以處理0到16384個槽澄步。
- 當數(shù)據(jù)庫的16384個槽都有節(jié)點處理時,集群處于上線狀態(tài)(ok)和泌;相反的村缸,如果數(shù)據(jù)庫中有任何一個槽沒得到處理,那么集群處于下線狀態(tài)(fail) 武氓。
- 當在redis集群放置一個key-value時梯皿,redis先對key使用crcy16算法計算出一個結(jié)果仇箱,然后把這個結(jié)果對16384求余數(shù),這樣每一個key都會對應一個編號在0-16383之間的槽东羹,redis會根據(jù)節(jié)點量大致均等的將槽映射到不同的節(jié)點中剂桥。
1.3 Redis cluster選舉:容錯
- 節(jié)點失效判斷:選舉過程是集群中所有master都參與,如果半數(shù)以上的master節(jié)點與master節(jié)點之間的通信超過(cluster-node-timeout)属提,就認為當前master節(jié)點掛掉渊额。
-
集群失效判斷:什么時候整個集群不可用(cluster_state:fail)?
- 如果集群任意master掛掉垒拢,且當前master沒有slave旬迹,則集群進入fail狀態(tài)。也可以理解為集群的[0-16383]slot映射不完全時進入fail狀態(tài)求类。
- 如果集群超過半數(shù)的master掛掉奔垦,無論是否有slave,集群進入fail狀態(tài)尸疆。
2. redis cluster 安裝
2.1 安裝Ruby環(huán)境
reids cluster需要使用集群管理redis-trib.rb椿猎,它的執(zhí)行相應依賴Ruby環(huán)境。
- 第一步:安裝ruby
yum install ruby
yum install rubygems
- 第二步:安裝ruby和redis的接口程序redis
gem install redis
安裝到這里就出現(xiàn)問題了寿弱,CentOS 7庫中的ruby的版本支持到2.0.0犯眠,可gem 安裝redis需要最低是2.2.2,下面采用rvm來更新ruby:
# 具體RVM安裝命令地址:http://rvm.io/
gpg --keyserver hkp://keys.gnupg.net --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3 7D2BAF1CF37B13E2069D6956105BD0E739499BDB # 安裝GPG秘鑰
curl -sSL https://get.rvm.io | bash -s stable # 安裝RVM
find / -name rvm -print # 查詢支持rvm的所有版本
rvm list known
rvm install 2.4.5 # 安裝2.4.5版本
rvm use 2.4.5 # 使用rvm 2.4.5
rvm use 2.4.5 --default # 設置2.4.5 為默認版本
rvm remove 2.3.5 # 刪除2.3.5這個版本
ruby --version # 查看ruby當前版本
-
第三步:復制redis-3.2.9/src/redis-trib.rb文件到/usr/local/redis-cluster目錄
前面我安裝的redis單機版是redis-3.2.9
cd /usr/local/
mkdir redis_cluster # 創(chuàng)建集群目錄
cp redis-3.2.9/src/redis-trib.rb /usr/local/redis_cluster/ -r
2.2 安裝RedisCluster
Redis集群最少需要三臺主服務器症革,三臺從服務器筐咧。
端口號分別為:7000~7001
- 第一步:在local目錄下創(chuàng)建 7000 7001 7002 7003 7004 7005,并且拷貝單機版安裝時噪矛,生成的bin目錄里面的所有文件量蕊,到7000 7001...文件中。
mkdir 7000 7001 7002 # 創(chuàng)建文件夾7000 70001 7002 ....
cp /redis/bin/* /usr/local/7000/ -r
cp /redis/bin/* /usr/local/7001/ -r
.......
-
第二步:修改7000艇挨、7001....目錄下的redis.conf配置文件残炮,修改端口:和目錄相對應7000、7001缩滨,打開Cluster-enable yes
- 第三步:啟動所有的實例
./redis-server redis.conf -p 7000
....
- 第四步:創(chuàng)建redis集群
./redis-trib.rb create --replicas 1 172.19.165.143:7000 172.19.165.143:7001 172.19.165.143:7002 172.19.165.143:7003 172.19.165.143:7004 172.19.165.143:7005
>>> Creating cluster
>>> Performing hash slots allocation on 6 nodes...
Using 3 masters:
172.19.165.143:7000
172.19.165.143:7001
172.19.165.143:7002
Adding replica 172.19.165.143:7003 to 172.19.165.143:7000
Adding replica 172.19.165.143:7004 to 172.19.165.143:7001
Adding replica 172.19.165.143:7005 to 172.19.165.143:7002
M: f649719d25e833a2cfa28877686334e4bee592b4 172.19.165.143:7000
slots:0-5460 (5461 slots) master
M: 87cbfb735fc468193e3e8fd06b028f7079478f16 172.19.165.143:7001
slots:5461-10922 (5462 slots) master
M: 5988d3d9be04d3b01b0b1028add0ee5b3ac793b8 172.19.165.143:7002
slots:10923-16383 (5461 slots) master
S: a9bc31025e9188826a4fde74f1379d10ac8f96de 172.19.165.143:7003
replicates f649719d25e833a2cfa28877686334e4bee592b4
S: 13bee88df50bf50a763d42135201c10e61762957 172.19.165.143:7004
replicates 87cbfb735fc468193e3e8fd06b028f7079478f16
S: 5fe655116e404a989c9bce4a4aa206b30eb65905 172.19.165.143:7005
replicates 5988d3d9be04d3b01b0b1028add0ee5b3ac793b8
Can I set the above configuration? (type 'yes' to accept): yes
>>> Nodes configuration updated
>>> Assign a different config epoch to each node
>>> Sending CLUSTER MEET messages to join the cluster
Waiting for the cluster to join...
>>> Performing Cluster Check (using node 172.19.165.143:7000)
M: f649719d25e833a2cfa28877686334e4bee592b4 172.19.165.143:7000
slots:0-5460 (5461 slots) master
1 additional replica(s)
S: a9bc31025e9188826a4fde74f1379d10ac8f96de 172.19.165.143:7003
slots: (0 slots) slave
replicates f649719d25e833a2cfa28877686334e4bee592b4
S: 13bee88df50bf50a763d42135201c10e61762957 172.19.165.143:7004
slots: (0 slots) slave
replicates 87cbfb735fc468193e3e8fd06b028f7079478f16
M: 5988d3d9be04d3b01b0b1028add0ee5b3ac793b8 172.19.165.143:7002
slots:10923-16383 (5461 slots) master
1 additional replica(s)
M: 87cbfb735fc468193e3e8fd06b028f7079478f16 172.19.165.143:7001
slots:5461-10922 (5462 slots) master
1 additional replica(s)
S: 5fe655116e404a989c9bce4a4aa206b30eb65905 172.19.165.143:7005
slots: (0 slots) slave
replicates 5988d3d9be04d3b01b0b1028add0ee5b3ac793b8
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
3. 命令客戶端連接集群
命令:
./redis-cli –h 127.0.0.1 –p 7000 –c
set key1 123
注意:-c 表示是以redis集群方式進行連接
./redis-cli -p 7001 -c
127.0.0.1:7001>get key1
-> Redirected to slot [9189] located at 127.0.0.1:7000
OK
127.0.0.1:7000>
4. 查看集群命令
查看集群狀態(tài)
127.0.0.1:7000> cluster info
cluster_state:ok
cluster_slots_assigned:16384
cluster_slots_ok:16384
cluster_slots_pfail:0
cluster_slots_fail:0
cluster_known_nodes:6
cluster_size:3
cluster_current_epoch:6
cluster_my_epoch:3
cluster_stats_messages_sent:926
cluster_stats_messages_received:926
查看集群中的節(jié)點:
127.0.0.1:7000> cluster nodes
5. 維護節(jié)點
集群創(chuàng)建成功后可以繼續(xù)向集群中添加節(jié)點
5.1 添加主節(jié)點
- 還是向前面一樣先創(chuàng)建7007節(jié)點
- 添加7007節(jié)點作為新節(jié)點
./redis-trib.rb add-node 127.0.0.1:7007 127.0.0.1:7000
- 查看集群節(jié)點會發(fā)現(xiàn)7007已添加在集群中
5.2 槽分配(數(shù)據(jù)遷移)
添加完主節(jié)點需要對主節(jié)點進行hash槽分配势就,這樣該主節(jié)才可以存儲數(shù)據(jù)。
給剛添加的7007結(jié)點分配槽:
- 第一步:連接上集群(連接集群中任意一個可用節(jié)點都可以)
./redis-trib.rb reshard 172.19.165.143 :7000
- 第二步:輸入要分配槽的數(shù)量
出現(xiàn)提示:How many slots do you want to move (from 1 to 16384)?這個提示時脉漏,直接輸入要分配的數(shù)量苞冯,如:3000。 - 第三步:輸入接收槽的結(jié)點id
這里準備給7007分配槽鸠删,通過cluster nodes 查看7007節(jié)點id:15b809eadae88955e36bcdbb8144f61bbbaf38fb
出現(xiàn)what is the receiving node ID?提示時輸入上面的id抱完。 - 第四步:輸入源節(jié)點id:all
注意:輸入源節(jié)點id,槽將從源節(jié)點中拿刃泡,分配后的槽在源節(jié)點中就不存在了巧娱,輸入all從所有源節(jié)點中獲取槽碉怔,輸入done取消分配。 - 第五部:輸入yes開始移動槽到目標結(jié)點id
5.3 添加從節(jié)點
添加7008從結(jié)點禁添,將7008作為7007主節(jié)點的從結(jié)點
命令:
./redis-trib.rb add-node --slave --master-id 主節(jié)點id 新節(jié)點的ip和端口 舊節(jié)點ip和端口(集群中任一節(jié)點都可以)
注意:如果原來該結(jié)點在集群中的配置信息已經(jīng)生成到cluster-config-file指定的配置文件中(如果cluster-config-file沒有指定則默認為nodes.conf)撮胧,這時可能會報錯:
[ERR] Node XXXXXX is not empty. Either the node already knows other nodes (check with CLUSTER NODES) or contains some key in database 0
解決方法是刪除生成的配置文件nodes.conf,刪除后再執(zhí)行./redis-trib.rb add-node指令
5.4刪除節(jié)點
./redis-trib.rb del-node 127.0.0.1:7005 節(jié)點id
刪除已經(jīng)占有hash槽的結(jié)點會失敗老翘,報錯:
[ERR] Node 127.0.0.1:7005 is not empty! Reshard data away and try again.
解決辦法芹啥,把已經(jīng)占用的槽分配出去
6. jedis連接集群
代碼實現(xiàn)
@Test
public void testJedisCluster() throws Exception {
//創(chuàng)建一連接,JedisCluster對象,在系統(tǒng)中是單例存在
Set<HostAndPort> nodes = new HashSet<>();
nodes.add(new HostAndPort("172.19.165.143", 7000));
nodes.add(new HostAndPort("172.19.165.143", 7001));
nodes.add(new HostAndPort("172.19.165.143", 7002));
nodes.add(new HostAndPort("172.19.165.143", 7003));
nodes.add(new HostAndPort("172.19.165.143", 7004));
nodes.add(new HostAndPort("172.19.165.143", 7005));
JedisCluster cluster = new JedisCluster(nodes);
//執(zhí)行JedisCluster對象中的方法铺峭,方法和redis一一對應墓怀。
cluster.set("cluster-test", "my jedis cluster test");
String result = cluster.get("cluster-test");
System.out.println(result);
//程序結(jié)束時需要關(guān)閉JedisCluster對象
cluster.close();
}
Spring
配置applicationContext.xml
<!-- 連接池配置 -->
<bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
<!-- 最大連接數(shù) -->
<property name="maxTotal" value="30" />
<!-- 最大空閑連接數(shù) -->
<property name="maxIdle" value="10" />
<!-- 每次釋放連接的最大數(shù)目 -->
<property name="numTestsPerEvictionRun" value="1024" />
<!-- 釋放連接的掃描間隔(毫秒) -->
<property name="timeBetweenEvictionRunsMillis" value="30000" />
<!-- 連接最小空閑時間 -->
<property name="minEvictableIdleTimeMillis" value="1800000" />
<!-- 連接空閑多久后釋放, 當空閑時間>該值 且 空閑連接>最大空閑連接數(shù) 時直接釋放 -->
<property name="softMinEvictableIdleTimeMillis" value="10000" />
<!-- 獲取連接時的最大等待毫秒數(shù),小于零:阻塞不確定的時間,默認-1 -->
<property name="maxWaitMillis" value="1500" />
<!-- 在獲取連接的時候檢查有效性, 默認false -->
<property name="testOnBorrow" value="true" />
<!-- 在空閑時檢查有效性, 默認false -->
<property name="testWhileIdle" value="true" />
<!-- 連接耗盡時是否阻塞, false報異常,ture阻塞直到超時, 默認true -->
<property name="blockWhenExhausted" value="false" />
</bean>
<!-- redis集群 -->
<bean id="jedisCluster" class="redis.clients.jedis.JedisCluster">
<constructor-arg index="0">
<set>
<bean class="redis.clients.jedis.HostAndPort">
<constructor-arg index="0" value="172.19.165.143"></constructor-arg>
<constructor-arg index="1" value="7000"></constructor-arg>
</bean>
<bean class="redis.clients.jedis.HostAndPort">
<constructor-arg index="0" value="172.19.165.143"></constructor-arg>
<constructor-arg index="1" value="7001"></constructor-arg>
</bean>
<bean class="redis.clients.jedis.HostAndPort">
<constructor-arg index="0" value="172.19.165.143"></constructor-arg>
<constructor-arg index="1" value="7002"></constructor-arg>
</bean>
<bean class="redis.clients.jedis.HostAndPort">
<constructor-arg index="0" value="172.19.165.143"></constructor-arg>
<constructor-arg index="1" value="7003"></constructor-arg>
</bean>
<bean class="redis.clients.jedis.HostAndPort">
<constructor-arg index="0" value="172.19.165.143"></constructor-arg>
<constructor-arg index="1" value="7004"></constructor-arg>
</bean>
<bean class="redis.clients.jedis.HostAndPort">
<constructor-arg index="0" value="172.19.165.143"></constructor-arg>
<constructor-arg index="1" value="7005"></constructor-arg>
</bean>
</set>
</constructor-arg>
<constructor-arg index="1" ref="jedisPoolConfig"></constructor-arg>
</bean>