槽指派
集群的整个数据库被分为16384个槽,数据库中的每个键都属于16384中的一个,clusterNode结构的slots属性和numslot属性记录了节点负责处理哪些槽
struct clusterNode{
unsigned char slots[16384/8];
int numslots;
}
slots属性是一个二进制位数组,2048个字节包含16384个二进制位。根据二进制位的值判断节点是否处理槽i,slots数组在索引i上的二进制位的值位1,那么表示节点负责处理槽i。
传播节点的槽指派信息
集群中每个节点都会保存每个槽被指派给了哪个节点
typedef struct clusterState{
clusterNode *slots[16384];
} clusterState;
- 如果slots[i]指针执行null,那么表示槽i尚未指派给任何节点。
- 否则表示i已经指派给clusterNode所代表的节点。
在集群中执行命令
当客户端节点发送与数据库键有关的命令时,接收命令的节点会计算出命令要处理的数据库键属于哪个槽,并检查这个槽是否指派给了自己
- 如果键所在的槽正好就指派给了当前节点,那么节点直接执行这命令
- 否则向客户端返回一个MOVED错误,指引客户端转向至正确的节点。
Redis重新分片
Redis重新分片可以将任意数量的已经指派给某个节点的槽
改为指派
给另一个节点,并且相关槽所属的键值对
也会从源节点被移动
到目标节点。
Redis集群的重新分片操作是由Redis的集群管理软件redis-trib负责执行的。重新分片步骤如下:
- redis-trib对目标节点发送 CLUSTER SETSLOT IMPORTING <source_id>命令,让目标节点准备好从源节点导入属于槽slot的键值对
- redis-trib对源节点发送CLUSTER SETSLOT MIGRATING<target_id> 命令, 让源节点准备好将属于槽slot的键值对迁移至目标节点
- redis-trib向源节点发送CLUSTER GETKEYSSINSLOT 命令获取最多count个属于slot的键值对的键名(key name)
- 对于步骤3获得的每个键名,redis-trib都向源节点发送一个MIGRAGET <target_id> <target_port> <key_name> 0 <time_out> 命令,被选中的键原子的从源节点迁移至目标节点。
- 重复3,4步骤,将slot的所有键值对迁移至目标节点。
- redis-trib向集群中的任意一个节点发送CLUSTER SETSLOT NODE <target_id> 命令,将槽slot指派给目标节点。这一指派信息会通过消息发送至整个集群。最终集群的所有节点都会知道槽slot已经指派给了目标节点。
重新分片过程中,属于被迁移的槽的键值对,可能一部分在源节点,一部分在目标节点。在源节点的键值对由源节点直接执行,如果源节点没有在自己的库找到指定的键,则向客户端返回ASK错误。指引客户端转向正在导入槽的目标节点。
MOVED和ASK
MOVED错误表示槽的负责权已经从一个节点转移到了另一个节点,而ASK错误只是在两个节点在迁移槽的时候使用的一种临时措施。
Redis复制与故障转移
Redis集群中的节点分为主节点和从节点,主节点用于处理槽,从节点用于复制某个主节点,并在被复制的主节点下线时,代替主节点继续处理命令请求。
故障检测
集群中的每个节点都会定期的向集群中的其他节点发送PING消息,其他节点返回PONG消息,来判断节点是否下线。如果没有收到PONG消息则标记为疑似下线。在集群中半数以上的主节点都将疑似下线节点标记为疑似下线。并向集群广播该节点已下线。
故障转移
当一个从节点发现正在复制的主节点已经下线时,从节点开始对主节点进行故障转移。
- 从节点中选中一个节点执行SLAVEOF no one,成为新的主节点。
- 新的主节点会撤销所有已下线主节点的槽指派,并将这些槽全部指向自己
- 新的主节点向集群中广播一条PONG消息,这个PONG消息可以让集群中的其他节点立刻知道这个节点已经由从节点变成了主节点,并且这个主节点已经接管了原本由已下线节点处理的槽。
选举新的主节点
- 集群的纪元是一个自增计数器,初始值为0;
- 某个节点开始故障转移时,集群配置纪元的值会被增加1
- 对于每个集群配置纪元,主节点都有一次投票的机会,第一个向主节点要求投票的从节点将获得主节点的投票
- 当从节点发现同步的主节点已经下线时会向集群发起投票请求
- 如果一个主节点具有投票权,并且这个主节点尚未投票给其他节点,主节点将票投给发起投票节点
- 当从节点获得超过半数的投票,这个从节点就会当选为新的主节点。
- 如果在一个配置纪元没有从节点获得足够多的投票,那么集群进入一个新的配置纪元。并再次进行选举,直到选出新的主节点。基于Raft算法的领头选举方法来
参考列表:
《Redis设计与实现(第二版)》