redis笔记⑤——redis集群

我们两清 提交于 2020-01-01 13:50:22

Redis Cluster高可用集群

概述

redis cluster集群是一个由多个主从节点群组成的分布式服务器群,它具有复制、高可用和分片特
性。

Redis cluster集群不需要sentinel哨兵节点也能完成节点移除和故障转移的功能。需要将每个节点
设置成集群模式,这种集群模式没有中心节点,可水平扩展,据官方文档称可以线性扩展到
1000节点。

redis cluster集群的性能和高可用性均优于之前版本的哨兵模式,且集群配置非常简单,其架构如下图所示:
在这里插入图片描述

集群的搭建

redis集群的搭建分两种方法,一种是原生的方法搭建,一种是使用redis提供的rb脚本搭建。

原生搭建

这里搭建四个节点的集群,两个主节点,两个从节点,首先还是配置每个redis节点,同样也是通过redis.conf文件配置。

配置节点

各个节点大部分配置还是和之前主从模式的节点配置差不多。

这里绑定的ip是我使用的虚拟机的ip,这样我的本机也是可以访问的
在这里插入图片描述
还是尽量减少RDB持久化的发生
在这里插入图片描述
同样如果我们配置了redis的密码还是要设置密码,不过不需要配置主机节点
在这里插入图片描述
在这里插入图片描述
接着开启集群模式
在这里插入图片描述
配置它的集群信息文件
在这里插入图片描述
打开这个配置
在这里插入图片描述
这个属性配置为no,这个配置的意思是在某一个节点出现问题挂掉的时候,如果配置no这个节点不可用,但其他节点可用,如果配置yes,代表整个集群都不可用,直到故障转移结束。
在这里插入图片描述

启动节点
  1. 启动节点

启动方式还是和普通节点的启动一样
在这里插入图片描述

  1. 节点互通

这时集群其实还没有搭建成功,此时这些几点之间还没有相互通信,先连接其中一个客户端
在这里插入图片描述
我们发现集群节点中只有自己
在这里插入图片描述
通过下面的命令连通其他的节点

cluster meet ip port

在这里插入图片描述
这里的连接时双向的,互通的,不需要各自和各自的节点手动连接,只要节点之间有其他节点相连都可以自动相连。现在各个节点之间都可以自由通信了,不过,现在的集群还没有搭建好,还没有分配槽点。
在这里插入图片描述

  1. 指派槽点

通过下面的节点指派槽点,redis总共有16384个槽点,这些槽点决定了那些数据存在哪些节点中,我们总共有两个主机节点,所以平分就是每个主机节点8192个槽点。这里如果手动分配那会很慢,可以通过脚本分配。
在这里插入图片描述
脚本如下
在这里插入图片描述
这里将槽点平分给7000和7001这两个节点
在这里插入图片描述

  1. 分配主从

上面我们看到只存在主机节点,没有从节点,所以我们需要分配主从,我们需要通过从机的客户端指定它的主机节点,这里指定的是节点的id,就是nodes信息里最前面的一串字符。
在这里插入图片描述

cluster replicate [nodeid]

在这里插入图片描述
现在我们发现主从已经分配好了
在这里插入图片描述

  1. 打开客户端

现在我们可以通过集群的方式打开客户端,只需要在最后加上-c就可以了
在这里插入图片描述
现在redis集群就搭建好了

使用redis提供的脚本

这里我们会搭建一个6个节点的集群,3主3从,而且在5.0之后redis提供了另一种搭建的办法,就不需要借助rb了,这里也是主要介绍的也是这种方式。

首先配置还是和之前一摸一样,就不再过多介绍了,我们直接从集群的搭建开始介绍。

这里有两种方式:

  1. 5.0之前redis提供的rb脚本,不过这种方式需要首先拥有一个rb环境,安装rb,然后通过rb脚本搭建集群
    (1)cd /usr/local/redis3/src
    (2)./redis-trib.rb create --replicas 1 127.0.0.1:9000 127.0.0.1:9001 127.0.0.1:9002 127.0.0.1:9003 127.0.0.1:9004 127.0.0.1:9005
  2. 5.0之后redis提供了另外的方式搭建集群,使用/usr/local/bin/redis-cli --cluster create 192.168.0.104:7000 192.168.0.104:7001 192.168.0.104:7002 192.168.0.104:7003 192.168.0.104:7004 192.168.0.104:7005 --cluster-replicas 1搭建,下面也会使用此方式搭建集群。

首先我们已经配置好了6个节点的配置文件,然后启动每一个节点。
在这里插入图片描述
只需要执行下面的命令就可以直接搭建集群(5.0以后),我们可以在帮助中查看这个命令的各种参数的说明。

/usr/local/bin/redis-cli -h 192.168.123.19 -p 8000 -a 123456 --cluster help

在这里插入图片描述
我们搭建集群需要使用的命令就是create命令,这里需要解释的是最后--cluster-replicas 1这个参数代表的是集群中主从节点的比率,1代表有多少主机就有多少从机,这里就是3主3从。

/usr/local/bin/redis-cli -h 192.168.123.19 -p 8000 -a 123456 --cluster create 192.168.123.19:8000 192.168.123.19:8001 192.168.123.19:8002 192.168.123.19:8003 192.168.123.19:8004 192.168.123.19:8005 --cluster-replicas 1

在这里插入图片描述
这时集群就已经搭建好了
在这里插入图片描述

集群伸缩

集群扩容

对于已经搭建好的集群我们可能还需要添加节点,这时我们就需要手动对这个集群扩容,下面我会演示对一个集群添加一个主节点和它的一个从节点。

同样有两种方式实现扩容,一种是原生的,一种是通过redis-cli的命令扩容。

  1. 原生的语法:cluster meet ip port
  2. redis-cli的命令:add-node 新节点ip 端口 已存在节点ip 端口

下面是演示的通过redis-cli的命令实现集群扩容

首先还是需要其他需要添加的节点
在这里插入图片描述
把节点加入集群
在这里插入图片描述
现在我们需要将从节点加入集群并且分配主从,这里可以采用先将从节点加入集群再分配集群,也可以通过redis-cli命令直接将从节点加入集群并一起分配主从,分配主从前面已经介绍了原生的方法,这里介绍redis-cli命令直接分配主从节点。

add-node 新节点ip  端口  已存在节点ip 端口  --cluster-slave --cluster-master-id masterID

迁移槽和数据

我们可以动态迁移槽和数据

  1. 首先开始槽迁移
/usr/local/bin/redis-cli --cluster reshard 192.168.123.19:8000

在这里插入图片描述

  1. 指定迁移的计划

首先是需要分配多少槽
在这里插入图片描述
选择接收的节点
在这里插入图片描述
接着就是输入槽点的源节点,我们分配的槽点就是由这些节点得来的,这里我们可以输入一个或多个源槽位的节点ID,输入完了就输入done表示结束,如果我们需要从所有其他的节点拿到槽位可以直接输入all,这里都是平均拿到槽位。
在这里插入图片描述

集群缩容

下线迁移槽

可以通过下面的命令将某个节点的一些槽位迁移给其他的节点

redis-cli --cluster reshard --cluster-from 要迁出节点ID  --cluster-to  接收槽节点ID --cluster-slots 迁出槽数量 已存在节点ip 端口
移除节点

可以通过下面的命令将节点移出集群,注意这里不仅会将节点移出集群而且还会关闭这个redis节点

redis-cli --cluster del-node 已存在节点ip:端口 要删除的节点ID

故障转移

redis集群的故障转移和哨兵模式下的故障转移类似,下面是故障转移的过程:

  1. 故障发现: 通过ping/pong消息实现故障发现(不依赖sentinel)
  2. 故障恢复
    1. 检查从节点的资格,每个从节点检查与主节点的断开时间,超过cluster-node-timeout*cluster-replica-validity-factor时间取消资格
    2. 选择偏移量最大的,替换主节点
    3. 撤销以前主节点的槽位,给新的主节点
    4. 向集群广播消息,表明已经替换了故障节点

集群客户端

在这里插入图片描述
smart客户端

  1. 从集群中选取一个可运行节点,使用cluster slots初始化槽和节点映射。

  2. 将cluster slots的结果映射到本地,为每个节点创建jedispool

  3. 准备执行命令

public class TestRedisCluster {
    public static void main(String[] args) {
        Logger logger= LoggerFactory.getLogger(TestRedisCluster.class);
        Set<HostAndPort> nodesList=new HashSet<>();
        nodesList.add(new HostAndPort("192.168.0.104",7000));
        nodesList.add(new HostAndPort("192.168.0.104",7001));
        nodesList.add(new HostAndPort("192.168.0.104",7002));
        nodesList.add(new HostAndPort("192.168.0.104",7003));
        nodesList.add(new HostAndPort("192.168.0.104",7004));
        nodesList.add(new HostAndPort("192.168.0.104",7005));

        // Jedis连接池配置
        JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
        // 最大空闲连接数, 默认8个
        jedisPoolConfig.setMaxIdle(200);
        // 最大连接数, 默认8个
        jedisPoolConfig.setMaxTotal(1000);
        //最小空闲连接数, 默认0
        jedisPoolConfig.setMinIdle(100);
        // 获取连接时的最大等待毫秒数(如果设置为阻塞时BlockWhenExhausted),如果超时就抛异常, 小于零:阻塞不确定的时间,  默认-1
        jedisPoolConfig.setMaxWaitMillis(3000); // 设置2秒
        //对拿到的connection进行validateObject校验
        jedisPoolConfig.setTestOnBorrow(false);
        JedisCluster jedisCluster=new JedisCluster(nodesList,jedisPoolConfig);
        System.out.println(jedisCluster.mset("k1", "v1", "k2", "v2", "k3", "v3"));
        System.out.println(jedisCluster.mget("k1","k2", "k3" ));
//        while (true) {
//            try {
//                String s = UUID.randomUUID().toString();
//                jedisCluster.set("k" + s, "v" + s);
//                System.out.println(jedisCluster.get("k" + s));
//                Thread.sleep(1000);
//            }catch (Exception e){
//                logger.error(e.getMessage());
//            }finally {
////                if(jedisCluster!=null){
////                    jedisCluster.close();
////                }
//            }
//        }
    }
}

下面是jedis集群客户端可能出现的问题:

  1. moved重定向:指我们发送命令时,会对发送的key进行crc16算法,得到一个数字,然而我们连接的客户端并不是管理这个数字的范围,所以会返回错误并告诉你此key应该对应的槽位,然后客户端需要捕获此异常,重新发起请求到对应的槽位。
  2. asx重定向:指在我们送发命令时,对应的客户端正在迁移槽位中,所以此时我们不能确定这个key是还在旧的节点中还是新的节点中。
  3. 如果我们使用mset之类的API实现批量操作的时候可能出现问题,因为如果我们操作的数据处于不同的节点中,就会报错。
    在这里插入图片描述

下面是jedis集群客户端的scan操作的API

public class TestRedisClusterScan {
    public static void main(String[] args) {
        Logger logger= LoggerFactory.getLogger(TestRedisCluster.class);
        Set<HostAndPort> nodesList=new HashSet<>();
        nodesList.add(new HostAndPort("192.168.0.104",7000));
        nodesList.add(new HostAndPort("192.168.0.104",7001));
        nodesList.add(new HostAndPort("192.168.0.104",7002));
        nodesList.add(new HostAndPort("192.168.0.104",7003));
        nodesList.add(new HostAndPort("192.168.0.104",7004));
        nodesList.add(new HostAndPort("192.168.0.104",7005));

        // Jedis连接池配置
        JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
        // 最大空闲连接数, 默认8个
        jedisPoolConfig.setMaxIdle(200);
        // 最大连接数, 默认8个
        jedisPoolConfig.setMaxTotal(1000);
        //最小空闲连接数, 默认0
        jedisPoolConfig.setMinIdle(100);
        // 获取连接时的最大等待毫秒数(如果设置为阻塞时BlockWhenExhausted),如果超时就抛异常, 小于零:阻塞不确定的时间,  默认-1
        jedisPoolConfig.setMaxWaitMillis(3000); // 设置2秒
        //对拿到的connection进行validateObject校验
        jedisPoolConfig.setTestOnBorrow(false);
        JedisCluster jedisCluster=new JedisCluster(nodesList,jedisPoolConfig);
        int hello = JedisClusterCRC16.getCRC16("hello");
        System.out.println(hello);
        System.out.println(hello%16384);
        Jedis connectionFromSlot = jedisCluster.getConnectionFromSlot(hello);
        System.out.println(connectionFromSlot);
        Map<String, JedisPool> clusterNodes = jedisCluster.getClusterNodes();
        Set<Map.Entry<String, JedisPool>> entries = clusterNodes.entrySet();
        Iterator<Map.Entry<String, JedisPool>> iterator = entries.iterator();
        while (iterator.hasNext()){
            Map.Entry<String, JedisPool> next = iterator.next();
            String key = next.getKey();
            JedisPool jedisPool = clusterNodes.get(key);
            System.out.println(key+"--"+jedisPool);
        }
    }
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!