接着上一篇,我们聊聊如何重新分片
所谓的重新分片就是说当集群中添加一个节点时,怎样给这个节点分配相应的槽位。以及在分配槽位的过程中该如何客户端发送过来的命令?本篇博客呢,就围绕这些问题进行展开讨论。
Q1:怎样重新分片,在分片过程中服务器是上进行,还是需要停机处理?
首先,redis在分片过程中是不需要停机处理的,在线上就可以完成分片的工作,也就是集群不需要下线。
所谓的重新分片就是把相应的槽位以及该槽位上所属的键值对从一个(源节点)迁移到另一个节点(目标节点)的过程,在迁移的过程中目标节点和源节点都可以处理客户端发来的命令请求。
redis集群的重新分片操作是由redis的集群管理软件redis-trib来完成的 。redis提供了重新分片所有的命令,redis-trib通过向目标节点和源节点发送命令来完成重新分片操作。
redis-trib对集群的单个槽slot的进行重新分片的步骤如下:
1)redis-trib对目标节点发送CLUSTER SETSLOT IMPORTING <source_id>命令,让目标节点准备好从源节点导入(import)属于槽slot的键值对。
2)redis-trib 对源节点发送CLUSTER SETSLOT MIGRATING <target_id>让源节点准备好将槽slot键值对迁移(migrate)至目标节点
3)redis-trib向源节点发送CLUSTER GETKEYSINSLOT 命令,获得最多count个属于槽slot的键值对的键名(key name)
4) 对于步骤3获得的每个键名,redis-trib都向源节点发送一个MIGRATE<target_ip><target_port><key_name>0命令,被选中的键原子的从源节点迁移至目标节点。
5)重复步骤3和4直到所有槽slot的键值对迁移完毕为止
6)redis-trib向集群中的任意一个节点发送CLUSTER SETSLOTNODE <target_id>命令,将槽slot指派给目标节点,这一指派消息会通过消息发送到集群中的每个节点,从而集群中的所有节点都知道slot已经指派给了目标节点。
Q2:如果在重新分片过程中客户端发送一个命令,比如说get key,正好命中该迁移中的槽,该如何处理这个命令?
源节点会先在自己的数据库里查找指定的key,如果找到的话就直接执行客户端的命令。如果没有找到的话,那么这个键很肯能已经迁移到目标节点,源节点会向客户端返回一个ASK错误,指引客户端转向导入槽的目标节点,客户端再次发送之前想要执行的命令。
Q3:对于Q2中具体实现的原理是什么呢?
这就是clusterState中两个重要的数组,一个是clusterNode importing_slots_from[],另一个是migrating_slots_to[]
当执行CLASTER SETSLOTimporting <source_id>时,目标节点会将importing_slots_from[i]设置为source_id对应的节点,同样的,当执行CLASTER SETSLOTmigrating<target_id>时,源节点会将migrating_slots_to[i]设置为taget_id所对应的节点。
比如说,客户端执行 get “hello”时,对应的槽位是 10000,发现该槽于节点node1上,node1首先查找数据库发现没有“hello”这个key,然后检查自己的migrating_slots_to[10000]发现不为空(node4)(为空的话会返回MOVED错误),那么node1会向客户端发送ASK错误,让他去找node4,此时客户端会打开自己的REDIS_ASKING表示,并向node4发送一个先发送ASKING命令,然后发送自己想要执行的命令。node4接收到客户端的命令,会检查10000是否已经指派给了自己,如果是的话,他就行直接执行客户端发送过来的命令。如果槽10000没有指派给自己,node4会检查自己的importing_slots_from[i],如果没有导入槽10000,那么返回MOVED错误。如果正在导入槽10000,此时检查客户端是否携带REDIS-ASKING标识,如果携带了,会执行客户端的命令,否则返回MOVED错误。