一、Redis槽分配与键定位
两个数据结构
1、clusterNode
typedef clusterNode{
...
unsigned char slots[16384/8]; //redis共有16384个槽
int numslots; //表示节点处理的槽数量
...
} clusterNode;
该数组共2048个字节,16384个二进制位(每个位代表一个索引槽)。每个节点上都对应一个clusterNode,其二进制位为1,表示该槽位由该节点处理,为0则该节点不处理。
每个节点均会向其他节点发送自己处理的槽位信息,其他节点会将收到的信息更新到clusterState.nodes中与发送节点对应的记录中。
2、clusterState
typedef struct clusterState{
...
clusterNode *slots[16384];
...
} clusterState;
其中slots[i]如果指向一个clusterNode表示该槽位已分配,否则(NULL)表示该槽位未分配。
用这两个数据结构主要是用于确定给定槽位是否已分配,其复杂度为O(1)。另外对于向其他节点广播自身处理的槽位信息时,无需遍历整个clusterState.slots数组来确定槽位与节点的对应关系,直接将自身clusterNode对应的slots数组发送即可。
确定键属于哪个槽
def slot_number(key) :
return CRC16(key) & 16383;
计算出key值应该去往哪个槽之后,需要确定哪个节点处理该键,此时由上述clusterState.slot[index]给定的节点clusterNode确定。如果指向的clusterNode就是接收节点本身,那么直接处理请求。如果不是,向客户端返回MOVED信息,并携带IP及端口信息,告诉客户端转向。在收到MOVED信息之后,客户端转而向目标节点发起处理请求。
Redis的批量处理相关
当多个请求的key同属一个槽位时,可以使用批量操作,其效率较单个发起请求的操作要高得多。
redis的数据结构clusterState中有另外一个量用来表示槽位号slot与键的关系
typedef struct clusterState{
...
zskiplist *slots_to_keys;
...
} clusterState;
利用此信息便可对属于某个或某些槽位的键进行批量操作。
重分片
在对集群进行水平扩容时需要进行重新分片,以使得槽位能分布到新加入的节点当中。
重分片可在线操作,而不影响现有业务的运行。
重分片操作可以通过Redis的集群管理软件redis-trib来执行。
重分片过程
重分片对应着Redis的部分槽位的转移,对应于该槽位的键转而由新的节点处理,从而每节点容纳的数据更多,以此达到扩容的目的。重分片的过程对应如下(由redis-trib执行相应命令):
1、告诉目标节点准备好从源节点导入属于slot的键值对
CLUSTER SETSLOT < slot> IMPORTING < source_id>
2、告诉源节点准备好将slot中的键值对迁移至目标节点
CLUSTER SETSLOT < slot> MIGRATING < target_id>
3、告诉源节点发送一定数目的键值对给目标节点
CLUSTER GETKEYSINSLOT < slot> < count>
4、对每个键,都向源节点发送MIGRATE < target_ip> < target_port> < key_name> 0 < timeout>将被选中的键原子地从源节点迁移至目标节点的slot中。
5、重复步骤3和4,直至slot中所有的key均迁移完成。
6、向集群中任意一个节点发送CLUSTER SETSLOT < slot> NODE < target_id>,该节点再将槽的分配信息更新到集群中所有其他节点。
二、故障检测与转移
Redis集群模式下,cluster-node-timeout参数(默认15秒)作为节点Fail的依据。
集群故障检测
集群中的每个节点都会定期地向其他节点发送PING报文,收到PING报文的节点会回复PONG报文。如果节点在规定时间内(上述timeout)没有收到某个节点的PONG报文,就将该节点标记为疑似下线状态PFAIL(probable fail)。其他节点通过集群状态信息交换,记录其他节点传来的节点疑似下线状态,当超过一半的节点将某个节点标记为疑似下线时,那么该节点就被标记为下线FAIL。然后进行FAIL标记的节点将该信息广播给其他节点,使其它节点都知道该节点已下线。
集群故障转移
在发现master节点下线后,会从所有从节点中通过选举产生新的主节点。
新的主节点更新之前指向下线主节点的槽指派关系,更新为指向自己。
向整个集群广播PONG消息,告知其他节点自己已成为新的主节点。
新的主节点全面接管已下线节点的槽位请求。
领导选举
从节点发现主节点下线之后,便要求其它主节点进行投票,以求备胎转正
1、具有投票权的节点:其它在线主节点,每个主节点只有一票可投
2、投给谁:收到的第一个发送CLUSTERMSG_TYPE_FAILOVER_AUTH_REQUEST的从节点,回复CLUSTERMSG_TYPE_FAILOVER_AUTH_ACK给从节点,表示认可其成为主节点
3、谁获胜:收到的CLUSTERMSG_TYPE_FAILOVER_AUTH_ACK报文个数大于等于N/2+1(N表示参与投票的主节点数)
如果一次投票完成后没选出来怎么办?
自增计数器值加1,进行再次选举,其他主节点重新投票,重复该过程直到新的主节点被选举出来。
参考
《Redis设计与实现》