视频1 视频21 视频41 视频61 视频文章1 视频文章21 视频文章41 视频文章61 推荐1 推荐3 推荐5 推荐7 推荐9 推荐11 推荐13 推荐15 推荐17 推荐19 推荐21 推荐23 推荐25 推荐27 推荐29 推荐31 推荐33 推荐35 推荐37 推荐39 推荐41 推荐43 推荐45 推荐47 推荐49 关键词1 关键词101 关键词201 关键词301 关键词401 关键词501 关键词601 关键词701 关键词801 关键词901 关键词1001 关键词1101 关键词1201 关键词1301 关键词1401 关键词1501 关键词1601 关键词1701 关键词1801 关键词1901 视频扩展1 视频扩展6 视频扩展11 视频扩展16 文章1 文章201 文章401 文章601 文章801 文章1001 资讯1 资讯501 资讯1001 资讯1501 标签1 标签501 标签1001 关键词1 关键词501 关键词1001 关键词1501 专题2001
redis分布式集群搭建介绍
2020-11-09 06:55:28 责编:小采
文档

Redis集群架构图

上图蓝色为redis集群的节点。

节点之间通过ping命令来测试连接是否正常,节点之间没有主区分,连接到任何一个节点进行操作时,都可能会转发到其他节点。

1、Redis的容错机制

节点之间会定时的互相发送ping命令,测试节点的健康状态,当节点接受到ping命令后,会返回一个pong字符串。

投票机制:如果一个节点A给节点B发送ping没有得到pong返回,会通知其他节点再次给B发送ping,如果集群中有超过一半的节点收不B节点的pong。那么就认为B节点挂了。一般会为每个节点提供一个备份节点,如果挂掉会切换到备份节点。

2、Redis集群存储原理

Redis对于每个存放的key会进行hash操作,生成一个[0-16384]的hash值(先进行

crc 算法再对16384取余)。

集群的情况下,就是把[0-16384]的区间进行拆分,放到不同的redis中。

3、Redis的持久化

Snapshotting:定时的将Redis内存中的数据保存到硬盘中

AOF:将所有的command操作保存到aof中,AOP的同步频率很高,数据即使丢失,粒度也很小,但会在性能上造成影响。

二、集群环境搭建

redis集群管理工具redis-trib.rb依赖ruby环境,首先需要安装ruby环境

安装ruby

yum install ruby
yum install rubygems

安装ruby和redis的接口程序

拷贝redis-3.0.0.gem至/usr/local下

执行:

gem install /usr/local/redis-3.0.0.gem

三、创建Redis集群

在一台服务器上,可以用不同端口号来表示不同redis服务器。

Redis集群最少需要三台服务器,而每台服务器有需要备用服务器,所以最少需要6台服务器。端口规划如下:

主服务器:192.168.100.66 :7001 :7002 :7003

从服务器:192.168.100.66 :7004 :7005 :7006

在/usr/local 创建文件夹用来存放服务器程序

mkdir 7001 7002 7003 7004 7005 7006

如果想让redis支持集群需要修改redis.config配置文件的cluster-enabled yes

本例中我们以端口来区别不同的redis服务,所以还需要修改redis.config的port为对应端口

修改完配置文件,将redis安装目录的bin复制到上面每个目录中。

分别进入7001/bin/ 7002/bin .....

启动服务./redis-server ./redis.conf

查看redis进程:ps -aux|grep redis 如下图则说明启动成功

创建集群:

将之前解压的文件夹的redis-3.0.0/src/redis-trib.rb复制到redis-cluster目录

运行

./redis-trib.rb create --replicas 1 192.168.100.66:7001 192.168.100.66:7002 192.168.100.66:7003 192.168.100.66:7004 192.168.100.66:7005 192.168.100.66:7006

如果执行时报如下错误:

[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的持久化文件后再重启redis,比如:appendonly.aof、dump.rdb

如果成功最终输入如下:

查询集群信息:

说明:

./redis-cli -c -h 192.168.101.3 -p 7001 ,其中-c表示以集群方式连接redis,-h指定ip地址,-p指定端口号

cluster nodes 查询集群结点信息

cluster info 查询集群状态信息

hash槽重新分配

第一步:连接上集群

./redis-trib.rb reshard 192.168.101.3:7001(连接集群中任意一个可用结点都行)

第二步:输入要分配的槽数量

输入 500表示要分配500个槽

第三步:输入接收槽的结点id

这里准备给7007分配槽,通过cluster nodes查看7007结点id为15b809eadae855e36bcdbb8144f61bbbaf38fb

输入:15b809eadae855e36bcdbb8144f61bbbaf38fb

第四步:输入源结点id

这里输入all

第五步:输入yes开始移动槽到目标结点id

添加从节点

集群创建成功后可以向集群中添加节点,下面是添加一个slave从节点。

添加7008从结点,将7008作为7007的从结点。

./redis-trib.rb add-node --slave --master-id 主节点id 添加节点的ip和端口 集群中已存在节点ip和端口

执行如下命令:

./redis-trib.rb add-node --slave --master-id cad9f7413ec6842c971dbcc2c48b4ca959eb5db4 192.168.101.3:7008 192.168.101.3:7001

cad9f7413ec6842c971dbcc2c48b4ca959eb5db4 是7007结点的id,可通过cluster nodes查看。

注意:如果原来该结点在集群中的配置信息已经生成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指令

查看集群中的结点,刚添加的7008为7007的从节点:

删除结点:

./redis-trib.rb del-node 127.0.0.1:7005 4b45eb75c8b428fbd77ab979b85080146a9bc017

删除已经占有hash槽的结点会失败,报错如下:

[ERR] Node 127.0.0.1:7005 is not empty! Reshard data away and try again.

需要将该结点占用的hash槽分配出去(参考hash槽重新分配章节)。

测试:

Maven:
<dependencies>
 <dependency>
 <groupId>redis.clients</groupId>
 <artifactId>jedis</artifactId>
 <version>2.7.0</version>
 </dependency>
 <!-- https://mvnrepository.com/artifact/junit/junit -->
 <dependency>
 <groupId>junit</groupId>
 <artifactId>junit</artifactId>
 <version>4.12</version>
 <scope>test</scope>
 </dependency>
 <!-- https://mvnrepository.com/artifact/org.springframework/spring-test -->
 <dependency>
 <groupId>org.springframework</groupId>
 <artifactId>spring-test</artifactId>
 <version>4.3.10.RELEASE</version>
 <scope>test</scope>
 </dependency>
</dependencies>

普通测试:

@Test
public void redisClusterTest1(){
 JedisPoolConfig config=new JedisPoolConfig();
 config.setMaxTotal(30);
 config.setMaxIdle(2);

 Set<HostAndPort> jedisNode=new HashSet<HostAndPort>();
 jedisNode.add(new HostAndPort("192.168.100.66",7001));
 jedisNode.add(new HostAndPort("192.168.100.66",7002));
 jedisNode.add(new HostAndPort("192.168.100.66",7003));
 jedisNode.add(new HostAndPort("192.168.100.66",7004));
 jedisNode.add(new HostAndPort("192.168.100.66",7005));
 jedisNode.add(new HostAndPort("192.168.100.66",7006));

 JedisCluster jc=new JedisCluster(jedisNode,config);
 jc.set("name","老王");
 String value=jc.get("name");
 System.out.println(value);
}

Spring测试:

配置文件:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
 <!-- 连接池配置 -->
 <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="192.168.100.66"></constructor-arg>
 <constructor-arg index="1" value="7001"></constructor-arg>
 </bean>
 <bean class="redis.clients.jedis.HostAndPort">
 <constructor-arg index="0" value="192.168.100.66"></constructor-arg>
 <constructor-arg index="1" value="7002"></constructor-arg>
 </bean>
 <bean class="redis.clients.jedis.HostAndPort">
 <constructor-arg index="0" value="192.168.100.66"></constructor-arg>
 <constructor-arg index="1" value="7003"></constructor-arg>
 </bean>
 <bean class="redis.clients.jedis.HostAndPort">
 <constructor-arg index="0" value="192.168.100.66"></constructor-arg>
 <constructor-arg index="1" value="7004"></constructor-arg>
 </bean>
 <bean class="redis.clients.jedis.HostAndPort">
 <constructor-arg index="0" value="192.168.100.66"></constructor-arg>
 <constructor-arg index="1" value="7005"></constructor-arg>
 </bean>
 <bean class="redis.clients.jedis.HostAndPort">
 <constructor-arg index="0" value="192.168.100.66"></constructor-arg>
 <constructor-arg index="1" value="7006"></constructor-arg>
 </bean>
 </set>
 </constructor-arg>
 <constructor-arg index="1" ref="jedisPoolConfig"></constructor-arg>
 </bean>
</beans>

测试类:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({"classpath:spring-config.xml"})
public class RedisClusterTest {
 @Autowired
 private JedisCluster jedisCluster;
 @Test
 public void redisClusterTest2(){
 jedisCluster.set("username","小明啦啦");
 String name=jedisCluster.get("username");
 System.out.println(name);
 }
}

更多redis知识请关注redis数据库教程栏目。

下载本文
显示全文
专题