Redis集群是Redis提供的分布式数据库方案,通过分片sharding来进行数据共享,并提供复制和故障转移功能。
什么是分片sharding?
集群分布式有什么用?
1、节点
- 节点其实就是运行在集群模式下的单个的redis服务器,通过cluster meet命令来组成一个集群。通过cluster-enabled 属性配置来设置为集群服务器。
- 节点继续使用redisServer 结构来保存服务器状态,此外用clusterNode、clusterLink、clusterState数据结构来存储集群模式中用到的的数据
- clusterNode结构保存一个节点的当前状态。其中clusterLink属性指向一个clusterLink结构,保存着其他相连节点的信息
- 每个节点都保存一个clusterState结构,其中的nodes属性指向每一个clusterNode结构
- cluster meet命令:相互添加到node字典中,发送接收成功握手
2、槽指派
Redis集群通过分片的方式来保存数据库中的键值对,集群的整个数据库被分为16384个槽,数据库中的每一个键都属于这164384中的一个。
- 通过 cluster addslots来指派槽。所有槽都被处理时,集群处于上线状态。
将0~5000 槽指派给节点 7000 负责
127.0.0.1:7000 > CLUSTER ADDSLOTS 0 1 2 ....5000
-
clusterNode结构的slots和numslot属性记录了节点负责处理哪些槽。
struct clusterNode{ .... unsigned char slots[16384/8]; int numslots; ..... }
值为 1 表示被负责处理。
- slots是个数组,getset某个槽的值复杂度都为O(1)。数组中的每一项都指向一个clusterNode结构。
-
节点相互通信告知槽指派信息,并更新对应的clusterNode结构。集群中的每个节点都知道每个槽被分派给了谁。
- clusterState中也有slots数组,记录集群中所有16384个槽的指派信息。如果需要查询某个槽的指派情况,用clusterState的slots查询复杂度为O(1)
- 当需要发送某个节点的槽指派情况,只需要查询该节点的slots数组,不需要遍历整个clusterState.slots数组。
3、在集群中执行命令
- 客户端向集群中的节点发送命令,如果命令中的键不在当前节点的指派槽中,就重定向至正确的节点,然后进行处理。
-
节点用
slot_number(key)
方法计算键属于哪个槽,获得槽i -
节点通过该槽的指针是否指向自己,来判断是否自己负责处理。
-
当客户端收到moved错误时,会根据ip port重定向到正确节点,并发送命令。节点转向就是换一个套接字来发送命令。
MOVED 1007 127.0.0.1:600
表示槽 1007 由端口号为 600 的节点负责 -
集群的客户端不会打印moved信息,自动转向。单机模式的客户端不了解 MOVED 作用,就会打印。
-
节点只能使用0号数据库。节点会用clusterState结构中的slots_to_keys跳跃表来保存槽和键之间的关系,方便对某个或者某些槽的数据库键进行批量操作。
4、重新分片
槽和键的关系?
- 重新分片可以将任意数量的,已经指派的槽,重新指派给另一个节点,并且槽所属的键值对也会从源节点被移动到目标节点。
- 重新分片是由redis-trib执行的。redis-trib先做好准备,获得需要重新分配的键值对的键名,然后对每个键名,都向源节点发送migrate命令,进行迁移。
- 重新分片可以在线进行,集群不需要下线,并且源节点和目标节点都可以继续处理命令请求。
5、ASK错误
- 如果源节点没有找到指定的键,说明该键有可能已经被迁移到了目标节点,源节点将向客户端返回一个ASK错误,指引客户端转向目标节点,并重发命令。
- 集群式的redis服务器不会打印ASK错误。
- clusterState结构中的importing_slots_from指针数组记录了正在导入的槽。该指针指向源节点。
- cluster_state结构的migrating_slots_to数组记录了当前节点正在迁移出去的槽。同理该指针也是指向目标节点。
- 如果节点没有在自己的数据库找到键key,那么节点就会检查自己的clusterState.migrating_slots_to[i],看key所属的槽i是否正在迁移,如果的确在迁移,则向客户端发送一个ASK错误,引导客户端到正在导入槽i的节点去查找键key。
- 意思就是我这边才迁移一半呢,就有另一个命令要来迁移这个键,我一查哎呀不在我这边了,已经给了别人了,你去别人那里找吧。
- Redis_ASKING命令是一个一次性的开关命令。
- MOVED错误表示这个槽从此不归我管了,以后都去找别人吧。ASK命令表示这次这个槽不在我这儿,我告诉你去找谁,但是下次你还是得来找我。
6、复制与故障转移
集群中的节点分为主节点和从节点,主节点用来处理槽,从节点用来复制某个主节点,当主下线时,从代替主处理命令。
- cluster REPLICATE
命令将接受命令的节点成为node_id的从节点,并开始对主进行复制。 - 从节点将clusterNode结构中的slaveOf指针指向主节点。然后将clusterState结构中的myself.flags属性从master改为slave。最后调用复制代码。
- 集群中所有节点都会在代表主节点的clusterNode结构的slaves和numslaves属性中记录正在复制这个主节点的从节点名单。
- 各个节点会相互ping,如果没有按时收到返回,那就标记接收方为疑似下线。clusterNode结构中fail_reports链表记录了所有其他节点对该节点的下线报告。每个下线报告由一个clusterNodeFailReport结构表示。如果超过半数的主节点都将某个主节点X报告为疑似下线,那么这个主节点将被标记为已下线,同时发送广播。
- 从对下线的主进行故障转移:丐帮有个长老死了,被他分舵下的弟子发现,该弟子广发英雄帖召集其他长老选出新长老,同时为自己拉票。当有超过半数的长老投票给他,他就成了新长老,以前的弟子就会重新指派给他来管理。同时再次广发英雄帖,告知帮中长老和弟子自己已成为新长老。和选举零头Sentinel的方法相似,都是基于Raft算法的领头选举算法。
7、消息
MEET、PING、PONG、FAIL、PUBLISH
- 消息头由clusterMsg结构表示。其中data属性指向clusterMsgData结构,是消息的正文。
- MEET、PING、PONG三中消息都使用相同的消息正文,所以节点通过消息头的type属性来判断。
- FAIL速度比Gossip协议快。
- 对集群中某个节点发送PUBLISH,将导致集群中所有节点都向channel频道发送message消息。