每个节点都会保存集群的数据信息,具体对象是clusterNode,这个对象里记录了每个节点创建时间、节点名、节点标识、节点IP、节点端口、负责的槽点等。
typedef 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;
//槽位
unsigned char slots[16384/8];
int numslots;
}clusterNode;
在集群中执行命令
客户端向节点发送命令时,节点会根据命令里的key先进行槽位的检查:
def CLUSTER_KEYSLOT(key):
//计算槽位
slot = slot_number(key)
//返回计算的槽位
reply_client(slot)
def slot_number(key):
return CRC16(key) & 16383
这里的CRC16用于计算key的CRC_16校验和(可参考CRC 校验的原理及功用),& 16383(有的版本是%计算?)用于计算0到16383之间的整数(也就是槽位),计算出槽位后,节点会检查自己在clusterState.slots数组中的值,发现是自己负责的槽位后则执行客户端命令,如果发现不是自己负责处理的槽位则返回‘MOVED’错误,指引客户端正确节点。MOVED格式为:
MOVED <slot> <ip>:<port>
为什么是16384个槽位
注:哈希槽是通过一张bitmap的形式来保存
- 如果槽位很大,比如为65536,发送心跳信息的消息头达8k,发送的心跳包过于庞大
- redis的集群主节点数量很大(基本不可能超过1000个)的话可能会造成网络拥堵,集群规模过大时,Gossip 协议的效率会显著下降,通信成本剧增。
- 槽位越小,节点少的情况下,压缩比高
故障检测
集群中的每个节点都会定期向其它节点发送PING消息,以此来检测对方是否在线,如果某节点没在规定时间内响应PONG消息,那么发送检测信息的节点会将对方标记为疑似下线(PFAIL)状态并记录在clusterNode的flag里。一旦半数以上的节点将某个节点都标记为了疑似下线状态,那其它节点会将这个节点由疑似下线标记为下线(FAIL)。
故障转移
当一个从节点发现正在复制的主节点是下线状态后,从节点会进行故障转移:
- 在之前主节点的所有从节点里选举出新的主节点。
- 新的主节点会撤销所有对收己下线主节点的槽指派,将这些槽全部指派给自己。
- 新的主节点向集群广播一条PONG消息,让其它从节点知道自己已成了主节点。
- 新的主节点开始接收和自己负责处理的槽有关的命令请求,完成故障转移。
选举主节点
- 当某个主节点出现故障下线的时候,等延迟一段时间后(下面进行说明),它下面的从节点会向集群里广播一条要求其它主节点进行投票的消息。
- 在同一轮投票中,每个主节点都有一次投票的机会,在它收到投票要求且自己还没进行投票时,它就会发起一次投票。
- 当一个从节点收集到大于等于N/2+1的投票时,那这个从节点就成为了新的主节点;如果一轮投票里未能产生过半的投票则会进入下一轮的投票,直到主节点选举出。
为什么要进行一段时间的延迟以及怎样做到延迟呢?
节点之间互相进行通信是会一个先后,某个节点出现了故障下线并不是所有节点都会在同一时间知道,假设某个主节点下线了它下面的一个从节点马上发起投票,其它主节点可能并不知道这个主节点已下线而不进行投票。同时进行一个延迟还可以以免所有从节点同时发起投票而导致平票。做到延迟是通过下面公式来的:
DELAY = 500ms + random(0 ~ 500ms) + SLAVE_RANK * 1000ms
其中的SLAVE_RANK指的是此slave已经从master复制数据的总量的rank。rank越小代表已复制的数据越新。一般来说,持有最新数据的slave将会首先发起选举。
redis避免脑裂
通过两个参数预防:
min-slaves-to-write 3
min-slaves-max-lag 10
第一个参数表示连接到master的最少slave数量
第二个参数表示slave连接到master的最大延迟时间
按照上面的配置,要求至少3个slave节点,且数据复制和同步的延迟不能超过10秒,否则的话master就会拒绝写请求
本文参考的有:
黄健宏的《Redis设计与实现》一书
其它链接:
redis高可用
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。