Redis集群实现原理

一、节点

最开始时,每个Redis实例自己是一个集群,连接各个节点的工作通过CLUSTER MEET 命令完成,A和B握手后,A会将B的信息通过Gossip协议传播给A所在集群中其他节点,让其他节点也与B握手;
Redis集群实现原理

// 集群的节点
struct clusterNode{
 
    // 创建节点的时间
    mstime_t ctime;
 
    // 节点名字
    char name[REDIS_CLUSTER_NAMELEN];
 
    // 节点状态
    int flags;
 
    // 配置纪元,用于故障转移
    uint64_t configEpoch;
 
    // 节点IP
    char ip[REDIS_IP_STR_LEN];
 
    // 节点端口号
    int port;
 
    // 连接的信息
    clusterlink *link;
 
};

// clusterNode的link属性
typedef struct clusterlink{
 
    // 连接的创建时间
    mstime_t ctime;
 
    // 套接字描述符
    int fd;
   
    // 输出缓冲区
    sds sndbuf;
 
    // 输入缓冲区
    sds rcvbuf;
 
    // 与这个连接关联的节点
    struct clusterNode *node;
 
} clusterlink;

每个节点还保存一个clusterState结构,即每个节点都保存着集群的状态;

typedef struct clusterState{
 
    clusterNode *myself;
 
    // 配置纪元
    uint64_t currentEpoch;
 
    // 集群状态,在线/下线
    int state;
 
    // 集群中至少处理着一个槽的节点数量
    int size;
   
    // 集群节点名单,Map<name,clusterNode>
    dict *nodes;
 
} clusterState;

二、槽指派

Redis集群通过分片保存键值对,集群的整个数据库分成16384(2^14)个槽,有任何一个槽没被节点处理,则集群处于下线状态。通过向节点发送CLUSTER ADDSLOTS 0 1 2 3 …… 100命令,可以将一个或多个槽指派给节点负责。一个节点除了会将自己负责的槽记录在clusterNode的slots里,还会发送消息告知集群其他节点;

struct clusterNode{
 
    // 用bitmap保存节点的槽
    unsigned char slots[16384/8];
 
    int numslots;
 
};

clusterState中的slots数组记录了集群所有槽的指派信息,而clusterNode.slots数组只记录了一个节点的槽指派;

typedef struct clusterState{
    // 记录了每个槽由哪个clusterNode管理
    clusterNode *slots[16384];
 
} clusterState;

Redis集群有16384个哈希槽,每个key通过CRC16校验后对16384取模来决定放置哪个槽(Slot),集群的每个节点负责一部分hash槽。客户端向节点发送命令时,如果键所在槽没有指派给当前节点,返回MOVED错误,指引客户端重定向到正确节点;

集群节点和单机数据库类似,也保存键值对字典和过期字典,节点只能使用0号数据库。节点还会用clusterState中的slots_to_keys跳表来保存槽与键的关系,保存哪个键在哪个槽里,分值是槽号,可以快速获得同一个槽里的若干个键;

typedef struct clusterState{
 
    zskiplist *slots_to_keys;
 
} clusterState;

三、重新分片

CLUSTER SETSLOT IMPORTING <source_id>命令从源节点导入槽slot;
CLUSTER SETSLOT MIGRATING <target_id>命令导出槽slot到目标节点;

typedef struct clusterState{
 
    // 当前节点正在从其他节点导入的槽
    clusterNode *importing_slots_from[16384];
 
    // 当前节点正在迁移的槽
    clusterNode *migrating_slots_to[16384];
 
} clusterState;

重新分片时会产生ASK错误,即由源节点负责的K-V转移到了目标节点里,需要重定向。和MOVED不同,MOVED表示槽的负责权转移了,ASK只是重新分片时的一种临时措施;

四、复制和故障转移

集群中可以用CLUSTER REPLICATE <node_id>让节点成为node_id的从节点,并开始对主节点复制,并告知集群其他节点;

每个节点定期向其他节点发送PING,如果一个主节点被半数以上主节点标记为下线,则标记为已下线。从它的从节点选举出新的主节点,和领头Sentinel的选举类似,先到先得,超过半数。然后执行SLAVE no one,成为新的主节点,负责处理槽,告知其他节点;

五、消息

节点间主要有MEET、PING、PONG、FAIL(下线)、PUBLISH(群发,客户端给某个节点发消息,这个节点再给其他节点发,不是客户端直接群发)五种消息;

上一篇:Linux下识别分区文件系统类型


下一篇:redis之集群