242_Redis_集群_cluster架构

举报
alexsully 发表于 2021/12/08 09:45:16 2021/12/08
【摘要】 cluster集群架构 slot概念 slot迁移 cluster应用

Redis cluster

去中心化, 集群有多个节点(主从Redis)组成,每个节点存储数据或多或少, 节点之间通过特殊的二进制协议交互集群信息

在搭建集群时,会为每一个分片的主节点,对应一个从节点,实现slaveof的功能,同时当主节点down,实现类似于sentinel的自动failover的功能

 

可能下线PFail & 确定下线Fail

Redis去中心化, 一个节点失联,不代表所有节点都认为它失联了, 集群需要一次协商, Redis采用Gossip协议来广播自己状态

  •   一个节点发现A节点失联(PFail) 发送广播给其它节点
  •   如果收到节点失联的节点数量(PFail count) 已经达到了集群大多数,标记该节点为确定下线(Fail,向整个集群广播 并进行主备切换

1 redis cluster配置修改

include /home/bigdata/redis.conf

port 6379

pidfile "/var/run/redis_6379.pid"

dbfilename "dump6379.rdb"

dir "/home/bigdata/redis_cluster"

logfile "/home/bigdata/redis_cluster/redis_err_6379.log"

cluster-enabled yes

cluster-config-file nodes-6379.conf

cluster-node-timeout 15000   # 当某个节点持续时间失联,认定其故障,需要主备切换

#cluster-slave-validity-factor  10  #容错紧急程度

#假设cluster-node-timeout=5,cluster-slave-validity-factor=10,则如果从节点跟主节点失联超过50秒,此从节点不能成为主节点

cluster-migration-barrier <count>:主节点需要的最小从节点数,只有达到这个数,主节点失败时,它从节点才会进行迁移

cluster-require-full-coverage <yes/no>

#在部分key所在的节点不可用时,如果此参数设置为”yes”(默认值), 则整个集群停止接受操作;

#如果此参数设置为”no”,则集群依然为可达节点上的key提供读操作

 

启动&将节点加入集群管理

# --replicas 1 采用最简单的方式配置集群,一个节点node 包括主从

redis-cli --cluster create --cluster-replicas 1 192.168.11.101:6379 192.168.11.101:6380 192.168.11.101:6381 192.168.11.101:6389 192.168.11.101:6390 192.168.11.101:6391

redis-cli -p 7000 cluster nodes | grep master  # 集群主节点状态

redis-cli -p 7000 cluster nodes | grep slave  # 集群从节点状态

2.1 普通方式登录

可能直接进入读主机,存储数据时,如果发现该key所在槽位不归自己管理, 会出现MOVED重定向操作 同时报错

redis-cli –p 6379

2.2  集群策略方式登录

-c 采用集群策略连接,设置数据会自动切换到相应的写主机

redis-cli -c -p 6379

cluster nodes命令查看集群信息

3 slots

Redis集群包含16384个插槽(hash slot),数据库中的每个键都属于16384个插槽的其中一个, 集群中的每个节点负责处理一部分插槽

集群使用公式 CRC16(key) % 16384 来计算键 key 属于哪个槽, 其中 CRC16(key)语句用于计算键 key CRC16 校验和

例 集群可以有主节点,其中:

  • 节点 A 负责处理 0 号至 5460 号插槽。
  • 节点 B 负责处理 5461 号至 10922 号插槽。
  • 节点 C 负责处理 10923 号至 16383 号插槽

3.1迁移大致流程

Slots 迁移的单位是槽, slot正在迁移时, 这个槽处于中间过渡状态, migrating –> importing  可以使用工具 (redis-trib)

  •     从源节点获取内容  迁移启动时候会将源节点和目标节点设置好中间过渡状态, 然后获取源节点槽位key列表, 再逐个进行迁移
  •     存到目标节点  当前key dump序列化, 传到目标端restore,返回给源端 ok
  •     从源节点删除内容 当源端收到ok后,删除该key

备注

目标端restore 到源端 删除key 期间, 源节点主线程处于阻塞状态,一直到key 删除, 如果迁移过程中网络异常,恢复后会提示用户继续迁移

由于过程中有阻塞, 每个key都很小, migrate指令会指向很快, 如果key很大,阻塞时间会很久, 导致源节点和目标节点卡顿.


3.2 迁移过程中的数据访问

  •    1  目标端和 源端的槽位可能都存在部分key数据
  •    2  客户端先访问源端, 如果key 存在,直接返回, 如果不在 两种情况 1目标端里 2 不存在,
  •    3  源端无法判断这两种情况, 反给客户端 –ASK targetNodeAddr的重定向指令
  •    4  客户端收到后,先去目标端执行不带任何参数的 ASKING指令
  •    5  重定向循环:不带参数 是因为迁移完成前 这个slot还不属于目标端, 如果直接发槽位命令,目标端会拒绝,反一个-MOVED 源节点
  •    6   AKSING 指令的目的是 告诉目标端不能置之不理,要当做自己的槽位来处理
  •    7 迁移影响效率,同样指令正常情况下1TTL , 迁移状态下 3TTL

 

3.3 槽位迁移感知

         如果槽位迁移过程中或者迁移完毕,客户端如何感知; 重试次数2次或者多次,Java/Python客户端 有重试参数设置

  •   1 客户端保存了槽位和节点的映射关系表, 要及时更新,方可把指令正确发送到节点上
  •   2 两个特殊错误error指令 MOVED & AKSING
  •   3 MOVED 用来纠正槽位:如果客户端发送命令给错误节点, 节点返回MOVED+节点, 客户端刷新自己的槽位表 然后重试指令
  •   4 ASKING 用来临时纠正槽位: 迁移过程中源端反 asking error携带上目标节点地址, 客户端不会刷新自己的槽位表, 后面还会去访问源端

3.4 集群变更感知

  如果服务器节点变更,客户端会立即得到通知,实时刷新节点表, 两种情况

  • 1 目标节点down, 客户端抛出一个 ConnectionError, 紧接着随机挑一个节点来重试,被重试节点通过MOVED 指令告知目标槽位被分配到新节点地址
  • 2 手动运维改变集群信息(节点迁移,移除), 客户端请求旧节点会收到ClusterDown错误,客户端会关闭所有连接, 清空槽位表,报错,待下一条指令请求,重新尝试初始化节点信息

4 在集群中录入值

  • redis-cli每次录入、查询键值,redis都会计算出该key应该送往的插槽,
  • 如果不是该客户端对应服务器的插槽,redis会报错,并告知应前往的redis实例地址和端口

  • redis-cli客户端提供了 c 参数实现自动重定向
  • redis-cli  -c p 6379 登入后,再录入、查询键值对可以自动重定向

4.1

不在一个slot下的键值,不能使用mget,mset等多键操作

可以通过{}来定义组的概念,从而使key中{}内相同内容的键值对放到一个slot中去

4.2

CLUSTER GETKEYSINSLOT <slot><count> 返回 count slot 槽中的键

5 添加 & 删除节点

#安装集群插件
EPEL源安装ruby支持
yum install ruby rubygems -y
使用国内源
gem sources -l
gem sources -a http://mirrors.aliyun.com/rubygems/
gem sources --remove https://rubygems.org/
gem sources -l
gem install redis -v 3.3.3

增加主节点
redis-cli --cluster add-node 192.168.11.101:6382 192.168.11.101:6379

转移slot(重新分片)
redis-cli --cluster reshard 10.0.0.54:7000

添加一个从节点

redis-cli --cluster add-node --cluster-slave --cluster-master-id 4e4452fedxxxxxxxxxxx
192.168.11.101:6392 192.168.11.101:6379

# redis-trib工具方式
redis-trib.rb add-node 127.0.0.1:7006 127.0.0.1:7000
redis-trib.rb reshard 127.0.0.1:7000
redis-trib.rb reshard 127.0.0.1:7000 49257f251824dd815bc7f31e1118b670365e861a 127.0.0.1:7006


删除节点先slot移动走

redis-trib.rb reshard 192.168.11.101:6379 xxxxxxxxxxxxxxxxx 192.168.11.101:6382
0-1364 5461-6826 10923-12287
1365 1366 1365

删除一个节点

删除master节点之前首先要使用reshard移除master的全部slot,然后再删除当前节点
redis-trib.rb del-node 192.168.11.101:6382  xxxxxxxxx
redis-trib.rb del-node 192.168.11.101:6392  xxxxxxxxx

6 故障恢复

如果主节点下线?从节点能否自动升为主节点?注意:15秒超时

主节点恢复后,主节点回来变成从机

如果所有某一段插槽的主从节点都宕掉,默认不能对外提供服务,涉及数据完整性, 如果想继续提供服务调整 redis.conf中的参数  cluster-require-full-coverage

如果某一段插槽的主从都挂掉,而cluster-require-full-coverage 为yes ,需要集群完整性,才能对外提供服务

如果某一段插槽的主从都挂掉,而cluster-require-full-coverage 为no ,那么,node 插槽数据全都不能使用,也无法存储。其它node可以对外提供服务

7 集群应用

import redis.clients.jedis.HostAndPort;
import redis.clients.jedis.JedisCluster;
import redis.clients.jedis.JedisPoolConfig;

import java.io.IOException;
import java.util.HashSet;
import java.util.Set;


public class JedisClusterDemo {
    public static void main(String[] args) throws IOException {

        JedisPoolConfig config = new JedisPoolConfig();
        config.setMaxTotal(20);
        config.setMaxIdle(10);
        config.setMinIdle(5);

        Set<HostAndPort> jedisClusterNode = new HashSet<HostAndPort>();
        jedisClusterNode.add(new HostAndPort("192.168.18.131", 8001));
        jedisClusterNode.add(new HostAndPort("192.168.18.131", 8004));
        jedisClusterNode.add(new HostAndPort("192.168.18.132", 8002));
        jedisClusterNode.add(new HostAndPort("192.168.18.132", 8005));
        jedisClusterNode.add(new HostAndPort("192.168.18.133", 8003));
        jedisClusterNode.add(new HostAndPort("192.168.18.133", 8006));

        JedisCluster jedisCluster = null;
        try {
            //connectionTimeout:指的是连接一个url的连接等待时间
            //soTimeout:指的是连接上一个url,获取response的返回等待时间   password = 123456
			//JedisCluster j = new JedisCluster(jedisClusterNode, connectionTimeout, soTimeout, maxAttempts, password, poolConfig);
            jedisCluster = new JedisCluster(jedisClusterNode, 6000, 5000, 10, "123456", config);
            System.out.println(jedisCluster.set("clusterArtisan", "artisanValue"));
            System.out.println(jedisCluster.get("clusterArtisan"));
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (jedisCluster != null)
                jedisCluster.close();
        }
    }
}

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

举报
请填写举报理由
0/200