节点间的通信
端口
集群中所有的节点都存储数据,并且参与集群状态的维护。为此,每个节点都提供了两个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 命令 |
数据结构
节点为了存储集群状态而提供的数据结构中,最关键的是 clusterNode 和 clusterState 结构:前者记录了一个节点的状态,后者记录了集群作为一个整体的状态。
clusterNode :保存了 一个节点的当前状态,包括创建时间、节点 id、ip 和端口号等。每个节点都会用一个
clusterNode结构记录自己的状态,并为集群内所有其他节点都创建一个clusterNode结构来记录节点状态typedef struct clusterNode {//节点创建时间mstime_t ctime;//节点idchar name[REDIS_CLUSTER_NAMELEN];//节点的ip和端口号char ip[REDIS_IP_STR_LEN];int port;//节点标识:整型,每个bit都代表了不同状态,如节点的主从状态、是否在线、是否在握手等int flags;//配置纪元:故障转移时起作用,类似于哨兵的配置纪元uint64_t configEpoch;//槽在该节点中的分布:占用16384/8个字节,16384个比特;每个比特对应一个槽:比特值为1,则该比特对应的槽在节点中;比特值为0,则该比特对应的槽不在节点中unsigned char slots[16384/8];//节点中槽的数量int numslots;…………} clusterNode;
除了上述字段,
clusterNode还包含节点连接、主从复制、故障发现和转移需要的信息等。clusterState :保存了在当前节点视角下,集群所处的状态。主要字段包括:
typedef struct clusterState {//自身节点clusterNode *myself;//配置纪元uint64_t currentEpoch;//集群状态:在线还是下线int state;//集群中至少包含一个槽的节点数量int size;//哈希表,节点名称->clusterNode节点指针dict *nodes;//槽分布信息:数组的每个元素都是一个指向clusterNode结构的指针;如果槽还没有分配给任何节点,则为NULLclusterNode *slots[16384];…………} clusterState;
除此之外,
clusterState还包括故障转移、槽迁移等需要的信息。
淘汰机制
当key的生存时间到时,并不会立即删除,而是采用下面的两种方式进行删除:
- 定期删除:Redis每隔一段时间就会查看设置了过期时间的key,一般会在100ms的间隔中默认查看3个key
- 惰性删除:当用户去查询一个已经过了生存时间的key时,Redis会先查看当前key的生存时间,如果生存时间已到,直接删除key,并且返回给用户一个空值
在Redis已经没有存储空间的情况下,添加一个新数据,Redis就会执行淘汰机制。Redis支持的淘汰机制有如下几种:
volatile-lru:当内存不足时,Redis会在过了生存时间的key中淘汰掉一个最近最少使用的keyallkeys-lru:当内存不足时,Redis会在全部的key中淘汰掉一个最近最少使用的keyvolatile-lfu:当内存不足时,Redis会在过了生存时间的key中淘汰掉一个最近最少频次使用的keyallkeys-lfu:当内存不足时,Redis会在全部的key中淘汰掉一个最近最少频次使用的keyvolatile-random:当内存不足时,Redis会在过了生存时间的key中随机淘汰掉一个keyallkeys-random:当内存不足时,Redis会在全部的key中随机淘汰掉一个key- `volatile-ttl:当内存不足时,Redis会在过了生存时间的key中淘汰掉一个剩余生存时间最少的key
noeviction(默认):当内存不足时,直接报错
通过maxmemory-policy策略的命令来执行具体使用的淘汰机制。同时,还可以通过maxmemory 字节大小来设置redis的最大内存
