节点间的通信

端口

集群中所有的节点都存储数据,并且参与集群状态的维护。为此,每个节点都提供了两个TCP端口:

  • 普通端口:主要用于为客户端提供服务,以及节点之间数据的迁移,通常定义为7000、70001等
  • 集群端口:通常定义为普通端口+10000,它主要用于节点之间的通信,如集群搭建、节点增减和数据迁移等操作时,节点之间的通信

协议

节点之间的通信协议通常可以分为点对点、广播、Gossip协议等类型,其中:

  • 点对点:两个节点之间的通信,其他的节点不参与它们之间的通信
  • 广播:向集群内所有的节点发送信息,优点在于集群收敛速度快,但每条消息需要发送给集群内所有的节点,消耗CPU、带宽等资源较多
  • Goosip协议:指在节点数量有限的网络中,每个节点都按照某种规则随机的与部分节点进行通信,经过一段时间的通信,每个节点的状态很快会达到一致。它的优点在于负载比广播方式低、去中心化、容错性高等,当时相比于广播方式,它的收敛速度要慢一些

集群收敛指集群内所有的节点获取到的信息是一致的。

消息

集群的节点采用每秒10次的定时任务进行通信工作,例如判断是否需要发送消息及消息的类型、确定接收节点、发送消息等。如果集群状态发生了变化,如果节点数量改变、槽状态改变等,通过节点之间的通信可以很快的让所有节点都收到消息,使集群快速收敛。

节点之间发送的消息主要分为如下5种,不同的消息类型、通信协议、发送的频率和时间、接收节点的选择等是不同的:

消息类型 描述
MEET 在节点握手阶段,当节点收到客户端的 CLUSTER MEET 命令时,会向新加入的节点发送 MEET 消息,请求新节点加入到当前集群;新节点收到 MEET 消息后会回复一个 PONG 消息
PING 集群里每个节点每秒钟会选择部分节点发送 PING 消息,接收者收到消息后会回复一个 PONG 消息。PING 消息的内容是自身节点和部分其他节点的状态信息,作用是彼此交换信息,以及检测节点是否在线。PING 消息使用 Gossip 协议发送,接收节点的选择兼顾了收敛速度和带宽成本,具体规则如下:随机找 5 个节点,在其中选择最久没有通信的 1 个节点;扫描节点列表,选择最近一次收到 PONG 消息时间大于 cluster_node_timeout / 2 的所有节点,防止这些节点长时间未更新
PONG PONG 消息封装了自身状态数据。可以分为两种:第一种 是在接到 MEET/PING 消息后回复的 PONG 消息;第二种 是指节点向集群广播 PONG 消息,这样其他节点可以获知该节点的最新信息,例如故障恢复后新的主节点会广播 PONG 消息
FAIL 当一个主节点判断另一个主节点进入 FAIL 状态时,会向集群广播这一 FAIL 消息;接收节点会将这一 FAIL 消息保存起来,便于后续的判断
PUBLISH 节点收到 PUBLISH 命令后,会先执行该命令,然后向集群广播这一消息,接收节点也会执行该 PUBLISH 命令

数据结构

节点为了存储集群状态而提供的数据结构中,最关键的是 clusterNodeclusterState 结构:前者记录了一个节点的状态,后者记录了集群作为一个整体的状态。

  • clusterNode :保存了 一个节点的当前状态,包括创建时间、节点 id、ip 和端口号等。每个节点都会用一个 clusterNode 结构记录自己的状态,并为集群内所有其他节点都创建一个 clusterNode 结构来记录节点状态

    1. typedef struct clusterNode {
    2. //节点创建时间
    3. mstime_t ctime;
    4. //节点id
    5. char name[REDIS_CLUSTER_NAMELEN];
    6. //节点的ip和端口号
    7. char ip[REDIS_IP_STR_LEN];
    8. int port;
    9. //节点标识:整型,每个bit都代表了不同状态,如节点的主从状态、是否在线、是否在握手等
    10. int flags;
    11. //配置纪元:故障转移时起作用,类似于哨兵的配置纪元
    12. uint64_t configEpoch;
    13. //槽在该节点中的分布:占用16384/8个字节,16384个比特;每个比特对应一个槽:比特值为1,则该比特对应的槽在节点中;比特值为0,则该比特对应的槽不在节点中
    14. unsigned char slots[16384/8];
    15. //节点中槽的数量
    16. int numslots;
    17. …………
    18. } clusterNode;
  • 除了上述字段,clusterNode 还包含节点连接、主从复制、故障发现和转移需要的信息等。

  • clusterState :保存了在当前节点视角下,集群所处的状态。主要字段包括:

    1. typedef struct clusterState {
    2. //自身节点
    3. clusterNode *myself;
    4. //配置纪元
    5. uint64_t currentEpoch;
    6. //集群状态:在线还是下线
    7. int state;
    8. //集群中至少包含一个槽的节点数量
    9. int size;
    10. //哈希表,节点名称->clusterNode节点指针
    11. dict *nodes;
    12. //槽分布信息:数组的每个元素都是一个指向clusterNode结构的指针;如果槽还没有分配给任何节点,则为NULL
    13. clusterNode *slots[16384];
    14. …………
    15. } clusterState;
  • 除此之外,clusterState 还包括故障转移、槽迁移等需要的信息。

Redis(9)——史上最强【集群】入门实践教程


淘汰机制

当key的生存时间到时,并不会立即删除,而是采用下面的两种方式进行删除:

  • 定期删除:Redis每隔一段时间就会查看设置了过期时间的key,一般会在100ms的间隔中默认查看3个key
  • 惰性删除:当用户去查询一个已经过了生存时间的key时,Redis会先查看当前key的生存时间,如果生存时间已到,直接删除key,并且返回给用户一个空值

在Redis已经没有存储空间的情况下,添加一个新数据,Redis就会执行淘汰机制。Redis支持的淘汰机制有如下几种:

  • volatile-lru:当内存不足时,Redis会在过了生存时间的key中淘汰掉一个最近最少使用的key
  • allkeys-lru:当内存不足时,Redis会在全部的key中淘汰掉一个最近最少使用的key
  • volatile-lfu:当内存不足时,Redis会在过了生存时间的key中淘汰掉一个最近最少频次使用的key
  • allkeys-lfu:当内存不足时,Redis会在全部的key中淘汰掉一个最近最少频次使用的key
  • volatile-random:当内存不足时,Redis会在过了生存时间的key中随机淘汰掉一个key
  • allkeys-random:当内存不足时,Redis会在全部的key中随机淘汰掉一个key
  • `volatile-ttl:当内存不足时,Redis会在过了生存时间的key中淘汰掉一个剩余生存时间最少的key
  • noeviction(默认):当内存不足时,直接报错

通过maxmemory-policy策略的命令来执行具体使用的淘汰机制。同时,还可以通过maxmemory 字节大小来设置redis的最大内存