《Redis设计与实现》读书笔记——复制、Sentinel、集群

1、复制

    用SLAVEOF命令让一个服务器(从)去复制另一个服务器(主)。例如127.0.0.1:12345> SLAVEOF 127.0.0.1 6379,127.0.0.1:12345将成为127.0.0.1:6379的从服务器。

    (1)旧版复制功能实现,分为同步和命令传播两个操作。当执行SLAVEOF时,首先执行同步操作,【1】从服务器向主服务器发送SYNC命令【2】主服务器执行BGSAVE,生成RDB文件,并用一个缓冲区记录新的写命令【3】将RDB文件发送给从服务器,从服务器载入RDB文件【4】将缓冲区的写命令发送给从服务器并执行。命令传播就是主服务器将自己执行的写命令发送给从服务器执行。旧版复制功能的缺陷是从服务器断线重连后,需要重传RDB文件


    (2)新版复制功能的实现,用PSYNC命令代替SYNC执行复制时的同步操作,有完整重同步和部分重同步两种模式。完整重同步用于初次复制,和SYNC一样;部分重同步用于断线重连后的复制。主从服务器各自维护一个复制偏移量来实现部分重同步,主服务器发送N字节、从服务器接受N字节都会将复制偏移量+N。


    复制积压缓冲区,是由主服务器维护的固定长度的队列,默认1 MB,保存最近的写命令。如果从服务器断线中产生的数据大于缓冲区大小,就进行完整重同步。

    除了复制偏移量、复制积压缓冲区外,实现部分重同步还需要服务器运行ID。是在服务器启动时生成,从服务器初次复制时,保存主服务器ID,重连时判断ID是否相等,即根据ID判断是不是一次重连,是执行部分重同步还是完整重同步。

    (3)复制的步骤

    【1】设置主服务器的地址和端口,例如SLAVEOF 127.0.0.1 6379,将127.0.0.1和6379保存在从服务器的masterhost和masterport属性里。SLAVEOF是一个异步命令,设置完成后从服务器返回OK,然后再执行复制工作

struct redisServer{

    // 主服务器地址
    char *masterhost;

    // 主服务器端口
    int masterport;

};

    【2】建立套接字连接【3】发送PING【4】身份验证【5】从服务器发送自己的监听端口,保存在redisClient里。从服务器作为一个redisClient保存在主服务器的redisServer.clients里【6】同步【7】命令传播

    【8】心跳检测:检测主从服务器的网络连接,辅助实现min-slaves选项(防止主服务器在不安全的情况下写),检测命令丢失;


2、Sentinel

    Sentinel(哨岗、哨兵)是Redis高可用性的解决方案,由一个或多个Sentinel实例组成的Sentinel系统可以监视多个主服务器以及下属的从服务器,并在监视的主服务器下线时,自动将下线的主服务器的某个从服务器升级为新的主服务器,旧的主服务器上线时成为从服务器。

    Sentinel本质上是运行在特殊模式下的Redis服务器,端口和命令表等不同。初始化Sentinel时会创建sentinelState,其中有一个master字典保存了所有被Sentinel监视的主服务器,键是主服务器名字,值是sentinelRedisInstance结构,表示被Sentinel监视的Redis服务器实例,可以是主服务器、从服务器或者另一个Sentinel。

    Sentinel会创建两个连向被监视主/从服务器的异步网络连接,一个是命令连接,一个是订阅连接。Sentinel默认10 s一次向被监视的主服务器发送INFO命令,通过回复获取主服务器的当前信息(包括各个主服务器的slaves)。

    Sentinel可以向主从服务器发送信息,可以接受来自主从服务器的频道信息。一个Sentinel可以分析接收到的频道信息来获知其他Sentinel的存在,所以用户在使用Sentinel时不需要提供各个Sentinel的地址信息,监视同一个服务器的多个Sentinel可以自动发现对方。Sentinel连接Sentinel是只创建命令连接。


    Sentinel默认每秒一次向与他创建了命令连接的主从服务器、Sentinel发送PING命令,判断它们是否在线。为了确认主服务器是否下线,它会向监视这一主服务器的其他Sentinel询问(每个Sentinel设置的PING响应时间不一样),如果接收到足够的下线判断,进行故障转移。故障转移由领头Sentinel执行,每个发现服务器下线的Sentinel都会要求其他Sentinel将自己设置为领头Sentinel,先到先得,超过半数则是领头,否则重新选举。故障转移选出新的主服务器时,选出复制偏移量最大的从服务器作为新的主服务器


3、集群

    (1)节点

    连接各个节点的工作通过CLUSTER MEET <ip> <port>命令完成,A和B握手后,A会将B的信息通过Gossip协议传播给A所在集群中其他节点,让其他节点也与B握手;

    集群模式下serverCron会调用集群模式特有的clusterCron函数;

    

// 集群的节点
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;

    (2)槽指派

    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;

    节点用键的CRC-16校验和计算槽号,CRC16(key) & 16383客户端向节点发送命令时,如果键所在槽没有指派给当前节点,返回MOVED错误,指引客户端重定向到正确节点

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

typedef struct clusterState{

    zskiplist *slots_to_keys;

} clusterState;

    (3)重新分片

    CLUSTER SETSLOT <slot> IMPORTING <source_id>命令从源节点导入槽slot;

    CLUSTER SETSLOT <slot> MIGRATING <target_id>命令导出槽slot到目标节点;

typedef struct clusterState{

    // 当前节点正在从其他节点导入的槽
    clusterNode *importing_slots_from[16384];

    // 当前节点正在迁移的槽
    clusterNode *migrating_slots_to[16384];

} clusterState;

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

    (4)复制和故障转移

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

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

    (5)消息

    节点间主要有MEET、PING、PONG、FAIL、PUBLISH五种消息;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值