本文为分布式Redis深度历险系列的第三篇,主要内容为Redis的Cluster,也就是Redis集群功能。java
Redis集群是Redis官方提供的分布式方案,整个集群经过将全部数据分红16384个槽来进行数据共享。node
一个集群由多个Redis节点组成,不一样的节点经过CLUSTER MEET
命令进行链接:git
CLUSTER MEET <ip> <port>
github
收到命令的节点会与命令中指定的目标节点进行握手,握手成功后目标节点会加入到集群中,看个例子,图片来自于Redis的设计与实现:redis
一个集群的全部数据被分为16384个槽,能够经过CLUSTER ADDSLOTS
命令将槽指派给对应的节点。当全部的槽都有节点负责时,集群处于上线状态,不然处于下线状态不对外提供服务。算法
clusterNode的位数组slots表明一个节点负责的槽信息。数组
struct clusterNode { unsigned char slots[16384/8]; /* slots handled by this node */ int numslots; /* Number of slots handled by this node */ ... }
看个例子,下图中一、三、五、八、九、10位的值为1,表明该节点负责槽一、三、五、八、九、10。缓存
每一个Redis Server上都有一个ClusterState的对象,表明了该Server所在集群的信息,其中字段slots记录了集群中全部节点负责的槽信息。服务器
typedef struct clusterState { // 负责处理各个槽的节点 // 例如 slots[i] = clusterNode_A 表示槽 i 由节点 A 处理 // slots[i] = null 表明该槽目前没有节点负责 clusterNode *slots[REDIS_CLUSTER_SLOTS]; }
能够经过redis-trib工具对槽从新分配,重分配的实现步骤以下:网络
CLUSTER GETKEYSINSLOT <slot> <count>
从源节点获取最多count个槽slot的keyMIGRATE <target_ip> <target_port> <key_name> 0 <timeout>
命令,将被选中的键原子的从源节点迁移至目标节点。在槽重分配的过程当中,槽中的一部分数据保存着源节点,另外一部分保存在目标节点。这时若是要客户端向源节点发送一个命令,且相关数据在一个正在迁移槽中,源节点处理步骤如图:
当客户端收到一个ASK错误的时候,会根据返回的信息向目标节点从新发起一次请求。
ASK和MOVED的区别主要是ASK是一次性的,MOVED是永久性的,有点像Http协议中的301和302。
咱们来看cluster下一次命令的请求过程,假设执行命令 get testKey
getCRC16(key) & (16384 - 1)
,获得槽信息后,会从一个缓存map中得到槽对应的redis server信息,若是能获取到,则调到第4步slots
命令以得到整个集群的槽分布信息,而后跳转到第2步重试命令以上步骤大体就是redis cluster下一次命令请求的过程,但忽略了一个细节,若是要查找的数据锁所在的槽正在重分配怎么办?
集群中每一个Redis节点都会按期的向集群中的其余节点发送PING消息,若是目标节点没有在有效时间内回复PONG消息,则会被标记为疑似下线。同时将该信息发送给其余节点。当一个集群中有半数负责处理槽的主节点都将某个节点A标记为疑似下线后,那么A会被标记为已下线,将A标记为已下线的节点会将该信息发送给其余节点。
好比说有A,B,C,D,E 5个主节点。E有F、G两个从节点。
当E节点发生异常后,其余节点发送给A的PING消息将不能获得正常回复。当过了最大超时时间后,假设A,B先将E标记为疑似下线;以后C也会将E标记为疑似下线,这时C发现集群中由3个节点(A、B、C)都将E标记为疑似下线,超过集群复制槽的主节点个数的一半(>2.5)则会将E标记为已下线,并向集群广播E下线的消息。
当F、G(E的从节点)收到E被标记已下线的消息后,会根据Raft算法选举出一个新的主节点,新的主节点会将E复制的全部槽指派给本身,而后向集群广播消息,通知其余节点新的主节点信息。
选举新的主节点算法与选举Sentinel头节点的过程很像:
最后,聊聊redis集群的其余两种实现方案。
客户端作路由,采用一致性hash算法,将key映射到对应的redis节点上。
其优势是实现简单,没有引用其余中间件。
缺点也很明显:是一种静态分片方案,扩容性差。
Jedis中的ShardedJedis是该方案的实现。
该方案在client与redis之间引入一个代理层。client的全部操做都发送给代理层,由代理层实现路由转发给不一样的redis服务器。
其优势是: 路由规则可自定义,扩容方便。
缺点是: 代理层有单点问题,多一层转发的网络开销
原文:Java架构笔记
免费Java高级资料须要本身领取,涵盖了Java、Redis、MongoDB、MySQL、Zookeeper、Spring Cloud、Dubbo高并发分布式等教程,一共30G。
传送门: https://mp.weixin.qq.com/s/JzddfH-7yNudmkjT0IRL8Q