《Redis 设计与实现》的总结
sentinel(哨兵)
哨兵集群:哨兵会每隔 1 秒给所有主从节点发送 PING 命令,如果哨兵没有收到响应,哨兵会认为节点出现故障了。通过多个哨兵节点一起判断,就可以避免单个哨兵因为自身网络状况不好,而误判主节点下线的情况。这多个哨兵组成一个哨兵集群。
主观下线与客观下线:
主观下线:当前哨兵认为主节点下线了。
客观下线:半数以上哨兵认为主节点下线了。
leader选举:当一个哨兵判断主节点下线了,此哨兵就会成为candidate,然后通知其他哨兵根据自己和主节点的网络连接情况,做出赞成投票或者拒绝投票的响应。当此哨兵收到半数以上支持的投票后,此哨兵就成为leader。
主从故障转移操作包含以下四个步骤:
- 第一步:挑选出一个从节点,转换为主节点。挑选原则:和主节点之间断网次数少的、配置高的、从主服务器复制到的内容多的、ID小的
- 第二步:其他「从节点」修改复制目标,修改为复制「新主节点」;客户端和哨兵建立连接后,客户端会订阅哨兵提供的频道。主从切换完成后,哨兵就会向
+switch-master
频道发布新主节点的 IP 地址和端口的消息,这个时候客户端就可以收到这条信息,然后用这里面的新主节点的 IP 地址和端口进行通信了。 - 第三步:将新主节点的 IP 地址和信息,通过「发布者/订阅者机制」通知给客户端;
- 第四步:当这个旧主节点重新上线时,将它设置为新主节点的从节点;
添加哨兵:
sentinel monitor <master-name> <ip> <redis-port> <quorum>
- 在主从集群中,主节点上有一个名为
__sentinel__:hello
的频道,不同哨兵就是通过它来相互发现,实现互相通信的。如,哨兵 A 把自己的 IP 地址和端口的信息发布到__sentinel__:hello
频道上,哨兵 B 和 C 订阅了该频道。那么此时,哨兵 B 和 C 就可以从这个频道直接获取哨兵 A 的 IP 地址和端口号。然后,哨兵 B、C 可以和哨兵 A 建立网络连接。 - 哨兵获取从节点信息:主节点知道所有「从节点」的信息,所以哨兵会每 10 秒一次的频率向主节点发送 INFO 命令来获取所有「从节点」的信息。
17.集群
17.1 简介
加入节点到集群
cluster-enabled # 是否开启集群模式的选项
CLUSTER MEET <ip> <port> # 告诉当前节点将ip:port节点加入到集群中
三个数据结构:
- clusterNode:clusterNode 结构保存了一个节点的当前状态,比如节点的创建时间、节点的名字、节点当前的配置纪元、节点的IP地址和端口号等等。
- clusterLink:clusterNode结构的link属性是一个clusterLink结构,该结构保存了连接节点所需的有关信息,比如套接字描述符,输人缓冲区和输出缓冲区。
redisClient结构中的套接字和缓冲区是用于连接客户端的,而clusterLink结构中的套接字和缓冲区则是用于连接节点的。 - clusterState:记录了在当前节点的视角下,集群目前所处的状态,例如集群是在线还是下线,集群包含多少个节点,集群当前的配置纪元,诸如此类:
三次握手:CLUSTER MEET <ip> <port>
进行三次握手的过程如下,PONG和PING相当于两个“确认收到”的信息:
17.2 槽
整个数据库被分为16384(2048*8)槽,只有这16384槽都有节点在处理时,集群才处于上线状态。下面将槽0至槽5000指派给节点:
CLUSTER ADDSLOTS 0 1 2 3 4 ... 5000
每个节点都会将自己负责的槽发送给其他节点,所以集群中的每个节点都会知道数据库中的16384个槽分别被指派给了集群中的哪些节点。
- clusterNode结构的slots属性和numslot属性记录了节点负责处理哪些槽。这样就可以通过少量的代价就可以告诉其他节点,当前节点负责哪些槽。
- clusterstate结构中的slots数组记录了集群中所有16384个槽都由哪个节点进行处理。用于快速得知某个槽是要由哪个节点负责的。
键属于哪个槽:CLUSTER KEYSLOT <key>
命令可以查看一个给定键属于哪个槽,计算算法为CRC16(key) & 16383
MOVED错误:如果当前节点不负责客户发送过来的key对应的槽,就会通过发送MOVED错误告诉客户处理该key的节点的ip和端口。
快速找到某个槽对应的所有key:clusterState结构中的slots_to_keys为跳跃表,用来保存槽和键之间的关系。slots_to_keys 跳跃表每个节点的分值(score)都是一个槽号,而每个节点的成员( member)都是一个数据库键。由于使用槽号进行排序,所以相同槽号的key都是紧挨在一起的,故通过slots_to_keys可以快速找到某个槽对应的所有key。
【注】在跳表中,每个member都对应一个score,而这个score将用来在跳表中进行排序。
迁移某槽对应的所有k-v:
- 1.通知接收方准备从哪里接收哪个槽,
- 2.通知发送方准备将哪个槽发送到哪里
- 3.从发送方获取最多count个属于槽slot的键值对的键名
- 4.将被选中的键原子地从源节点迁移至且标节点。重复3和4,直到将槽中所有key传送到接收方。
- 5.告诉集群中所有节点,此槽现在是被接收方处理。
通过CLUSTER DELSLOTS <slot> [slot ...]
和CLUSTER DELSLOTS <slot> [slot ...]
,应该就会触发上面的迁移过程。
客户访问的key对应的槽正在迁移时:如果客户访问的key对应的槽正在迁移,那么如果槽中不存在的key,就说明key已经迁移到其他节点了,此时就会通过发送ASK错误告诉客户处理该key的节点的ip和端口。接收到ASK错误的客户端会先向节点发送ASKING命令,然后才发送要执行的命令。如果不先发送ASKING命令,节点就会返回MOVED错误。
- clusterState结构的importing_slots_from 数组记录了当前节点正在从其他节点导入的槽;
- clusterState结构的migrating_slots_to数组记录了当前节点正在迁移至其他节点的槽;
17.3 复制与故障转移
主节点与从节点:
- 设置从节点:
CLUSTER REPLICATE <node_id>
将接收命令的节点变成node_id的从节点。 - 主节点下线:集群中的每个节点都会定期地向集群中的其他节点发送PING消息,规定时间内没有接收到PONG,就将接收PING的节点设为疑似下线。半数以上节点认为节点C下线了,那么就通知集群节点C下线了。
- 主节点选举:主节点下线之后,会从所有从节点中选择一个成为主节点。成为从节点以后,会像集群广播成为从节点的这个信息。选取主节点的方法:使用raft算法从所有从节点中选取一个成为主节点,不过具有投票权的不是从节点,而是其他主节点。
此选举leader过程与sentinel有一些不一样,这里应该也可以使用sentinel中的方式选取leader,不过没有必要。
节点直接的五种消息:
- MEET:请求加入集群。
- PING:确认其他节点是否还活着。
- PONG:确认这条MEET或者PING已到达,或广播告诉其他节点,当前节点的新状态,如
- FALL:发送消息,通知某个节点已下线
- PUBLISH:一个命令,接收该命令的节点会执行该命令。