1. Redis集群
1.1 概述
Redis3.0以后推出Redis cluster集群方案。
1.2 Reids cluster 架构图
架构细节
- 所有reids节点彼此互联(PING-PONG机制),内部使用二进制协议传输和带宽。
- 节点的fail是通过集群中超过半数的节点检测失效时才生效。
- 客户端与redis节点直连,不需要中间件proxy,客户端不需要连接集群所有节点,连接集群中任何一个可用节点即可。
- reids-cluster把所有的物理节点映射到[0-16383]slot上,cluster负责维护node<->slot<->value
- 集群的整个数据库被分为了16384个槽(slot),数据库的每个键都属于这16384槽中的一个,集群中的每个节点可以处理0到16384个槽。
- 当数据库的16384个槽都有节点处理时,集群处于上线状态(ok);相反的,如果数据库中有任何一个槽没得到处理,那么集群处于下线状态(fail) 。
- 当在redis集群放置一个key-value时,redis先对key使用crcy16算法计算出一个结果,然后把这个结果对16384求余数,这样每一个key都会对应一个编号在0-16383之间的槽,redis会根据节点量大致均等的将槽映射到不同的节点中。
1.3 Redis cluster选举:容错
- 节点失效判断:选举过程是集群中所有master都参与,如果半数以上的master节点与master节点之间的通信超过(cluster-node-timeout),就认为当前master节点挂掉。
-
集群失效判断:什么时候整个集群不可用(cluster_state:fail)?
- 如果集群任意master挂掉,且当前master没有slave,则集群进入fail状态。也可以理解为集群的[0-16383]slot映射不完全时进入fail状态。
- 如果集群超过半数的master挂掉,无论是否有slave,集群进入fail状态。
2. redis cluster 安装
2.1 安装Ruby环境
reids cluster需要使用集群管理redis-trib.rb,它的执行相应依赖Ruby环境。
- 第一步:安装ruby
yum install ruby
yum install rubygems
- 第二步:安装ruby和redis的接口程序redis
gem install redis
安装到这里就出现问题了,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 # 创建集群目录
cp redis-3.2.9/src/redis-trib.rb /usr/local/redis_cluster/ -r
2.2 安装RedisCluster
Redis集群最少需要三台主服务器,三台从服务器。
端口号分别为:7000~7001
- 第一步:在local目录下创建 7000 7001 7002 7003 7004 7005,并且拷贝单机版安装时,生成的bin目录里面的所有文件,到7000 7001...文件中。
mkdir 7000 7001 7002 # 创建文件夹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
....
- 第四步:创建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. 查看集群命令
查看集群状态
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
查看集群中的节点:
127.0.0.1:7000> cluster nodes
5. 维护节点
集群创建成功后可以继续向集群中添加节点
5.1 添加主节点
- 还是向前面一样先创建7007节点
- 添加7007节点作为新节点
./redis-trib.rb add-node 127.0.0.1:7007 127.0.0.1:7000
- 查看集群节点会发现7007已添加在集群中
5.2 槽分配(数据迁移)
添加完主节点需要对主节点进行hash槽分配,这样该主节才可以存储数据。
给刚添加的7007结点分配槽:
- 第一步:连接上集群(连接集群中任意一个可用节点都可以)
./redis-trib.rb reshard 172.19.165.143 :7000
- 第二步:输入要分配槽的数量
出现提示:How many slots do you want to move (from 1 to 16384)?这个提示时,直接输入要分配的数量,如:3000。 - 第三步:输入接收槽的结点id
这里准备给7007分配槽,通过cluster nodes 查看7007节点id:15b809eadae88955e36bcdbb8144f61bbbaf38fb
出现what is the receiving node ID?提示时输入上面的id。 - 第四步:输入源节点id:all
注意:输入源节点id,槽将从源节点中拿,分配后的槽在源节点中就不存在了,输入all从所有源节点中获取槽,输入done取消分配。 - 第五部:输入yes开始移动槽到目标结点id
5.3 添加从节点
添加7008从结点,将7008作为7007主节点的从结点
命令:
./redis-trib.rb add-node --slave --master-id 主节点id 新节点的ip和端口 旧节点ip和端口(集群中任一节点都可以)
注意:如果原来该结点在集群中的配置信息已经生成到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,删除后再执行./redis-trib.rb add-node指令
5.4删除节点
./redis-trib.rb del-node 127.0.0.1:7005 节点id
删除已经占有hash槽的结点会失败,报错:
[ERR] Node 127.0.0.1:7005 is not empty! Reshard data away and try again.
解决办法,把已经占用的槽分配出去
6. jedis连接集群
代码实现
@Test
public void testJedisCluster() throws Exception {
//创建一连接,JedisCluster对象,在系统中是单例存在
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);
//执行JedisCluster对象中的方法,方法和redis一一对应。
cluster.set("cluster-test", "my jedis cluster test");
String result = cluster.get("cluster-test");
System.out.println(result);
//程序结束时需要关闭JedisCluster对象
cluster.close();
}
Spring
配置applicationContext.xml
<!-- 连接池配置 -->
<bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
<!-- 最大连接数 -->
<property name="maxTotal" value="30" />
<!-- 最大空闲连接数 -->
<property name="maxIdle" value="10" />
<!-- 每次释放连接的最大数目 -->
<property name="numTestsPerEvictionRun" value="1024" />
<!-- 释放连接的扫描间隔(毫秒) -->
<property name="timeBetweenEvictionRunsMillis" value="30000" />
<!-- 连接最小空闲时间 -->
<property name="minEvictableIdleTimeMillis" value="1800000" />
<!-- 连接空闲多久后释放, 当空闲时间>该值 且 空闲连接>最大空闲连接数 时直接释放 -->
<property name="softMinEvictableIdleTimeMillis" value="10000" />
<!-- 获取连接时的最大等待毫秒数,小于零:阻塞不确定的时间,默认-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>