Redis 集群

Redis集群是Redis提供的分布式数据库方案,通过分片sharding来进行数据共享,并提供复制和故障转移功能。

什么是分片sharding?

集群分布式有什么用?

1、节点

  1. 节点其实就是运行在集群模式下的单个的redis服务器,通过cluster meet命令来组成一个集群。通过cluster-enabled 属性配置来设置为集群服务器。
  2. 节点继续使用redisServer 结构来保存服务器状态,此外用clusterNode、clusterLink、clusterState数据结构来存储集群模式中用到的的数据
  3. clusterNode结构保存一个节点的当前状态。其中clusterLink属性指向一个clusterLink结构,保存着其他相连节点的信息
  4. 每个节点都保存一个clusterState结构,其中的nodes属性指向每一个clusterNode结构
  5. cluster meet命令:相互添加到node字典中,发送接收成功握手

2、槽指派

Redis集群通过分片的方式来保存数据库中的键值对,集群的整个数据库被分为16384个槽,数据库中的每一个键都属于这164384中的一个。

  1. 通过 cluster addslots来指派槽。所有槽都被处理时,集群处于上线状态。

    将0~5000 槽指派给节点 7000 负责

    127.0.0.1:7000 > CLUSTER ADDSLOTS 0 1 2 ....5000

  2. clusterNode结构的slots和numslot属性记录了节点负责处理哪些槽。

    struct clusterNode{
       ....
       unsigned char slots[16384/8];
       int numslots;
      .....
    }
    

值为 1 表示被负责处理。

  1. slots是个数组,getset某个槽的值复杂度都为O(1)。数组中的每一项都指向一个clusterNode结构。

  2. 节点相互通信告知槽指派信息,并更新对应的clusterNode结构。集群中的每个节点都知道每个槽被分派给了谁。

  1. clusterState中也有slots数组,记录集群中所有16384个槽的指派信息。如果需要查询某个槽的指派情况,用clusterState的slots查询复杂度为O(1)

  1. 当需要发送某个节点的槽指派情况,只需要查询该节点的slots数组,不需要遍历整个clusterState.slots数组。

3、在集群中执行命令

  1. 客户端向集群中的节点发送命令,如果命令中的键不在当前节点的指派槽中,就重定向至正确的节点,然后进行处理。

  2. 节点用slot_number(key)方法计算键属于哪个槽,获得槽i

  3. 节点通过该槽的指针是否指向自己,来判断是否自己负责处理。

  4. 当客户端收到moved错误时,会根据ip port重定向到正确节点,并发送命令。节点转向就是换一个套接字来发送命令。

    MOVED 1007 127.0.0.1:600 表示槽 1007 由端口号为 600 的节点负责

  5. 集群的客户端不会打印moved信息,自动转向。单机模式的客户端不了解 MOVED 作用,就会打印。

  6. 节点只能使用0号数据库。节点会用clusterState结构中的slots_to_keys跳跃表来保存槽和键之间的关系,方便对某个或者某些槽的数据库键进行批量操作。

4、重新分片

槽和键的关系?

  1. 重新分片可以将任意数量的,已经指派的槽,重新指派给另一个节点,并且槽所属的键值对也会从源节点被移动到目标节点。
  2. 重新分片是由redis-trib执行的。redis-trib先做好准备,获得需要重新分配的键值对的键名,然后对每个键名,都向源节点发送migrate命令,进行迁移。
  3. 重新分片可以在线进行,集群不需要下线,并且源节点和目标节点都可以继续处理命令请求。

5、ASK错误

  1. 如果源节点没有找到指定的键,说明该键有可能已经被迁移到了目标节点,源节点将向客户端返回一个ASK错误,指引客户端转向目标节点,并重发命令。
  2. 集群式的redis服务器不会打印ASK错误。
  3. clusterState结构中的importing_slots_from指针数组记录了正在导入的槽。该指针指向源节点。
  4. cluster_state结构的migrating_slots_to数组记录了当前节点正在迁移出去的槽。同理该指针也是指向目标节点。
  5. 如果节点没有在自己的数据库找到键key,那么节点就会检查自己的clusterState.migrating_slots_to[i],看key所属的槽i是否正在迁移,如果的确在迁移,则向客户端发送一个ASK错误,引导客户端到正在导入槽i的节点去查找键key。
  6. 意思就是我这边才迁移一半呢,就有另一个命令要来迁移这个键,我一查哎呀不在我这边了,已经给了别人了,你去别人那里找吧。
  7. Redis_ASKING命令是一个一次性的开关命令。
  8. MOVED错误表示这个槽从此不归我管了,以后都去找别人吧。ASK命令表示这次这个槽不在我这儿,我告诉你去找谁,但是下次你还是得来找我。

6、复制与故障转移

集群中的节点分为主节点和从节点,主节点用来处理槽,从节点用来复制某个主节点,当主下线时,从代替主处理命令。

  1. cluster REPLICATE 命令将接受命令的节点成为node_id的从节点,并开始对主进行复制。
  2. 从节点将clusterNode结构中的slaveOf指针指向主节点。然后将clusterState结构中的myself.flags属性从master改为slave。最后调用复制代码。
  3. 集群中所有节点都会在代表主节点的clusterNode结构的slaves和numslaves属性中记录正在复制这个主节点的从节点名单。
  4. 各个节点会相互ping,如果没有按时收到返回,那就标记接收方为疑似下线。clusterNode结构中fail_reports链表记录了所有其他节点对该节点的下线报告。每个下线报告由一个clusterNodeFailReport结构表示。如果超过半数的主节点都将某个主节点X报告为疑似下线,那么这个主节点将被标记为已下线,同时发送广播。
  5. 从对下线的主进行故障转移:丐帮有个长老死了,被他分舵下的弟子发现,该弟子广发英雄帖召集其他长老选出新长老,同时为自己拉票。当有超过半数的长老投票给他,他就成了新长老,以前的弟子就会重新指派给他来管理。同时再次广发英雄帖,告知帮中长老和弟子自己已成为新长老。和选举零头Sentinel的方法相似,都是基于Raft算法的领头选举算法。

7、消息

MEET、PING、PONG、FAIL、PUBLISH

  1. 消息头由clusterMsg结构表示。其中data属性指向clusterMsgData结构,是消息的正文。
  2. MEET、PING、PONG三中消息都使用相同的消息正文,所以节点通过消息头的type属性来判断。
  3. FAIL速度比Gossip协议快。
  4. 对集群中某个节点发送PUBLISH,将导致集群中所有节点都向channel频道发送message消息。