在大数据高并发场景下,Sentinel存在一些问题,写请求全部落在 Master 节点,Master 节点就一个实例,存储容量、CPU、内存、IO都存在瓶颈,如果我们扩容内存,会导致RDB文件过大,从而fork子进程时会阻塞较长时间。此时Redis 集群方案应运而生。
1.数据如何分片?
Redis Cluster 采用的是虚拟槽分区,一个集群共有 16384 个哈希槽,Redis Cluster会自动把这些槽平均分布在集群实例上。例如,如果集群中有 N 个实例,那么,每个实例上的槽个数为 16384/N个。
![](https://i-blog.csdnimg.cn/blog_migrate/ec2363e9d3a8f2c9d551942506f83f7e.png)
Redis Cluster 会对 key 进行 hash 得到一个整数值,然后用这个整数值对 16384 进行取模来得到具体槽位。
2.扩容机制
如图所示,6379、6380、6381三个主节点,6382是6379的从节点,6383是6380的从节点,6384是6381的从节点,现在因为业务发展过快,需要进行扩容,我们新增一个主节点6385和一个从节点6386。
![](https://i-blog.csdnimg.cn/blog_migrate/7b55e6504337a290feb05a50526b80ff.png)
步骤一:首先需要为新节点指定槽的迁移计划,也就是将哪些节点的哪些槽迁移到新节点中。并且迁移计划要确保每个节点负责相似数量的槽,从而保证各节点的数据均匀。槽迁移计划确定后开始逐个把槽内数据从源节点迁移到目标节点中。
![](https://i-blog.csdnimg.cn/blog_migrate/66d5bade988979ce67cfba58bcad5d76.png)
![](https://i-blog.csdnimg.cn/blog_migrate/008003fc42677ca7275861d5972cbc79.png)
如上图所示,6379准备把自己的4097-5460槽迁移给新节点6385,6380准备把自己的9558-10921槽迁移给新节点6385,6381准备把自己的15019-16383槽迁移给新节点6385。
步骤二:迁移数据数据迁移过程是逐个槽进行的,每个槽迁移的流程如下流程说明:
![](https://i-blog.csdnimg.cn/blog_migrate/5f693c9ac0dacce87b24af377d00d2d9.png)
①对目标节点发送cluster setslot {slot} importing {sourceNodeId}命令,让目标节点准备导入槽数据。
②对源节点发送cluster setslot {slot} migrating {targetNodeId}命令,让源节点准备迁出槽数据。
③源节点循环执行cluster getkeysinslot {slot} {count}命令,获取count个数据槽{slot}的键。
④在源节点上执行migrate {targetIp} {targetPort} key 0 {timeout} 命令把指定key迁移
注意:Redis3.2.8后,使用pipeline传输
⑤重复步骤3、4直到槽下所有的键值数据迁移到目标节点。
⑥向集群内所有主节点发送cluster setslot {slot} node {targetNodeId}命令,通知槽分配给目标节点。
3.缩容机制
缩容的三种情况:
①下线迁移槽
②忘记节点
③关闭节点
槽迁移和扩容一样
4.MOVED
当客户端向一个错误的节点发出了指令,该节点会发现指令的 key 所在的槽位并不归自己管理,这时它会向客户端发送一个 MOVED 指令并携带目标操作的节点地址,告诉客户端去连这个节点去获取数据。
客户端收到 MOVED 指令后,要立即纠正本地的槽位映射表。后续所有 key 将使用新的槽位映射表。
案例:
首先计算出哈希值,然后取模16384,得到槽
![](https://i-blog.csdnimg.cn/blog_migrate/e9aca838de614a272e77e7ce34f1e7ca.png)
计算哈希值命令:
127.0.0.1:6379>cluster keyslot "hello world"
命中
![](https://i-blog.csdnimg.cn/blog_migrate/07dfbb59170a2b1331588eb511353099.png)
未命中
![](https://i-blog.csdnimg.cn/blog_migrate/bd4513c6b17bdba08f9ca59208193017.png)
MOVED命令里包含两个信息,一个是槽的位置,一个是目标节点地址。
5.Asking
![](https://i-blog.csdnimg.cn/blog_migrate/74da3f85fb6b03aa2d0ca4f164f41667.png)
集群的伸缩(扩容/缩容)的时候,当我们去源节点访问时,发现key已经迁移到目标节点,会回复ask转向异常,收到异常后,先是执行asking命令,然后给目标节点再次发送命令,然后就会返回结果。
假如我们执行一个get key命令,这个key原来是在A节点,现在迁移到B节点,然后会给我们返回ASK转向异常,当我们收到ASK转向异常后,需要执行一条Asking命令给目标节点,然后在发送get命令。
ASK与MOVED共同点:两者都是重定向
ASK与MOVED不同点:
- MOVED:槽已经确定
- Asking:槽在迁移过程,key有可能在source节点有可能在target节点
6.槽位迁移感知
如果集群中某个槽位正在迁移或者已经迁移完了,客户端如何能感知到槽位的变化?
客户端保存了槽位和节点的映射关系表,当客户端收到moved指令的时候,他会去刷新槽位映射关系表,获取到最新的映射关系。当收到ask转向异常时,不会刷新槽位映射关系表,因为它是临时纠正。
7.容错
Redis Cluster 可以为每个主节点设置若干个从节点,单主节点故障时,集群会自动将其中某个从节点提升为主节点。如果某个主节点没有从节点,那么当它发生故障时,集群将完全处于不可用状态。不过 Redis 也提供了一个参数cluster-require-full-coverage可以允许部分节点故障,其它节点还可以继续提供对外访问。