Redis(十一)——集群模式

1、概念

集群是Redis的分布式方案,通过分片来进行数据共享,并提供复制和故障转移功能。

主从模式的读写分离提高了读并发,但是写的上限还是一台redis服务器,集群模式则可以提高写能力。

2、启动集群节点

Redis服务器启动时根据cluster-enabled配置选择开启后作为单机模式还是集群模式。集群模式下,一开始各个服务器节点相互独立,通过命令连接形成集群。A节点向B节点发送命令【cluster meet <ip> <port>】进行握手,这里的ip+port是B节点的。

3、集群数据结构

(1)clusterNode表示一个节点的各种基本信息,包括创建时间、地址、端口、配置纪元、名字、所连的节点等信息,属性【clusterLink* link】表示所连的节点。

typedef struct clusterNode{
    //创建节点的时间
    mstime_t ctime;
    //节点名字,40个十六进制符
    char name[40];
    //各种标识,主从节点、上线下线
    int flags;
    //配置纪元,用于故障转移
    unit64_t configEpoch;
    //ip地址
    char ip[REDIS_IP_STR_LEN];
    //端口
    int port;
    //保存连接节点所需的有关信息
    clusterLink* link;
    //哪些槽归自己处理
    unsigned char slots[16384/8];
    //本节点处理多少个槽
    int numslots;
    //...
} clusterNode;

(2)clusterLink保存了连接节点所需的有关信息,其中缓冲区是给节点用的,而不是客户端。

    typedef struct clusterLink{
        //创建连接的时间
        mstime_t ctime;
        //TCP套接字描述符
        int fd;
        //输出缓冲区,保存要发给其他节点的信息
        sds sndbuf;
        //输入缓冲区,保存从其他节点接收到的信息
        sds rcvbuf;
        //连接关联的节点,没有则设空
        struct clusterNode* node;
    }clusterLink;

(3)clusterState是每个节点都会有的结构,记录的是集群信息,nodes字典包含了全部的节点。

typedef struct clusterState{
    //指向当前节点的指针
    clusterNode* myself;
    //配置纪元
    unit64_t currentEpoch;
    //集群状态
    int state;
    //集群中至少处理一个槽的节点数量
    int size;
    //集群节点名单,key为节点名,val为clusterNode结构
    dict* nodes;
    //每个槽都归哪个节点管?
    clusterNode *slots[16384];
    //...
}clusterState;

4、握手过程

A将B添加到自己的clusterState.nodes字典里,然后给B发meet消息;

B接收A的消息,添加A到自己的clusterState.nodes字典里,返回一个pong消息;

A再回一条ping消息,握手成功。

  

5、槽

Redis集群通过分片来保存键值对,整个数据库被分为16384个槽(slot),每个节点处理0-16384个槽,全部槽都有节点处理时集群才处于上线状态(ok),否则处于下线状态(fail)。

(1)槽数据结构

单个节点clusterNode有属性记录自己负责处理哪些槽,并且会群发给其他集群节点,每个节点都知道各个槽都归谁管。

unsigned char slots[16384/8];//二进制位数组,1表示处理,0表示不处理
int numslots;//本节点负责的槽点数量

集群clusterState纪录各个槽都归谁管。

clusterNode* slots[16384];//要么指向NULL,要么指向节点

clusterNode和clusterState都有slots数组设计巧妙,缺一不可。

  • 缺少clusterNode.slots:想知道某个节点处理的槽:需要遍历clusterState.slots。
  • 缺少clusterState.slots:想知道某个槽归谁管:需要遍历clusterState.nodes字典,遍历每个clusterNode.slots进行判断。

(2)槽指派(分片)

通过命令【cluster addslots <slot> [slot ...]】,例如【cluster addslots 1 2】。

如果指派的槽只要有一个被指派过了则返回错误,因此该命令不会覆盖,重新指派槽有其他操作。设置完clusterState.slots和clusterNode.slots之后,会群发告知其他节点哪些槽归我管。

(3)计算键属于哪个槽

CRC16(key)&16383

(4)维护槽和key的关系

clusterState.slots_to_key是个跳跃表,分值score是槽号。

(5)重新分片

重新指派槽操作是由redis的集群管理软件redis-trib负责执行的,单个槽迁移距离:节点A要往B迁移槽100的健值对。

  • 1)通知B准备好从A接入数据 「cluster setslot 100 importing A.id」
  • 2)通知A准备好迁出数据给B 「cluster setslot 100 migrating B.id」
  • 3)找A获取最多count个槽100的键名 「cluster getkeysinslot 100 count」
  • 4)步骤3获取的每个键名都向B发送命令迁移数据 「migrate B.id B.port key 0 timeout」
  • 5)重复执行步骤3和步骤4,直到所有属于槽100的键都迁移完成
  • 6)随便找个节点通知槽100已经指派给节点B cluster setslot 100 node B.id

(6)MOVE错误

向节点A操作key=name的键,节点A发现这个key所在的槽归节点B管,集群模式会隐藏错误,自动重定向B,执行客户端的命令,返回客户端的结果会提示重定向的节点。MOVE错误的解决是永久的,下次客户端要操作key=name的键就直接去找B了。

(7)ASK错误

发生在槽迁移的过程中,类似MOVE错误,会隐藏错误且自动重定向。但是ASK错误的解决是一次性的,下次客户端还是找A节点操作。

 

6、故障检测

集群节点之间会有心跳检测,如果超过半数的主节点都认为A下线了,则A会被标记为「已下线」,并广播通知。

7、故障转移

和主从模式差不多,选出新节点,新的主节点会把槽都指派给自己,广播通知:「老子上位了,这些槽都归老子管」

8、新增一个节点会发生什么?

  • 先将该节点加入到集群中,与各个节点建立连接。此时新节点并不处理槽。
  • 集群重分配:对集群与新节点重新分配槽,使各个节点处理的槽数量尽可能相同。
  • 数据迁移:将原节点的槽数据迁移到新节点上,迁移完成后就可以承载读写负载。
  • 客户端重新定位:客户端查询key会打到新的节点上。

 

posted @ 2020-11-15 00:21  守林鸟  阅读(789)  评论(0编辑  收藏  举报