先看一张图,大致触摸下Redis Cluster
Redis Cluster要求至少需要3个master才能组成一个集群,同时每个master至少需要有一个slave节点。各个节点之间保持TCP通信。当master发生了宕机, Redis Cluster自动会将对应的slave节点提拔为master,来重新对外提供服务。
Redis Cluster 功能 : 负载均衡,故障切换,主从复制 。
先说下槽,集群中每个redis实例都负责接管一部分槽,总槽数为:16384(2^14),如果有3台master,那么每台负责5461个槽(16384/3)。
redis节点 | 负责的槽位 |
---|---|
节点1 | 0-5461 |
节点2 | 5461-10922 |
节点3 | 10922-16383 |
当redis客户端设置值时,会拿key进行CRC16算法,然后 跟16384取模,得到的就是落在哪个槽位,根据上面表格就得出在哪台节点上。槽公式如下:
slot = CRC16(key) & 16383
Redis集群中,每个节点都会有其余节点ip,负责的槽 等 信息。
JedisCluster配置只用指定集群中某一个节点的IP,端口信息就可以了。JedisCluster初始化时,会找配置的节点获取整个集群的信息(cluster nodes命令)。
解析集群信息,得到集群中所有master信息,然后遍历每台master,通过ip,端口构建jedis实例,然后put到一个全局nodes变量里面(Map类型) , key为ip,端口,值为Jedis实例,nodes值如下:
nodes={172.19.93.120:6380=redis.clients.jedis.JedisPool@74ad1f1f,.....}
在上面遍历master过程中,还做一件事,遍历此台master负责的槽索引,然后又put到一个全局map slots里面。值为上面的Jedis实例, slots值如下:
slots={0=redis.clients.jedis.JedisPool@74ad1f1f,
1=redis.clients.jedis.JedisPool@74ad1f1f,
2=redis.clients.jedis.JedisPool@74ad1f1f,
....
5461 = redis.clients.jedis.JedisPool@65aa1f2f, ####另外的master机器
....
16383=redis.clients.jedis.JedisPool@756d1afd}
有了上面的slots变量,当有值set 时, 会先算出slot = getCRC16(key)&(16383-1),假如是12182 , 然后调用slots.get(12182) 得到jedis实例,然后去操作redis。
如果发现MovedDataException,说明初始化得到的槽位与节点的对应关系有问题,(节点新增或者宕机)就会重置slots。
集群机器等数据信息通常有两种方式,一种是集中式,比如springcloud服务集群信息保存在配置中心 。另一种就是redis的方式,gossip。
集中式:好处在于,元数据的更新和读取,时效性非常好,一旦元数据出现了变更,立即就更新到集中式的存储中,其他节点读取的时候立即就可以感知到; 不好在于,所有的元数据的跟新压力全部集中在一个地方,可能会导致元数据的存储有压力。
gossip:好处在于,元数据的更新比较分散,不是集中在一个地方,更新请求会陆陆续续,打到所有节点上去更新,有一定的延时,降低了压力; 缺点,元数据更新有延时,可能导致集群的一些操作会有一些滞后。
通信的端口就是本身redis监听端口+10000 ,比如 监听端口6379,通信端口就是16379 。
Gossip协议的主要职责就是信息交换。信息交换的载体就是节点彼此发送的Gossip消息,常用的Gossip消息可分为:ping
消息、pong
消息、meet
消息、fail
消息等。
举例当新增一个节点,也就是Meet消息过程
举例当一个节点故障,怎么判断下线
集群中的每个节点都会定期向其他节点发送ping命令,如果接受ping消息的节点在指定时间内没有回复pong,则发送ping的节点就把接受ping的节点标记为主观下线。
如果集群半数以上的主节点都将主节点A标记为主观下线,则节点A将被标记为客观下线(通过节点的广播)即下线。
当一个从节点发现自己正在复制的主节点进入了已下线状态时,从节点将开始对下线主节点进行故障转移,以下是故障转移执行的步骤:
主从复制简单步骤
全量复制过程
服务器运行ID(runid)
每个Redis节点(无论主从),在启动时都会自动生成一个随机ID(每次启动都不一样),由40个随机的十六进制字符组成;runid用来唯一识别一个Redis节点。通过info Server命令,可以查看节点的runid:
主从节点初次复制时,主节点将自己的runid发送给从节点,从节点将这个runid保存起来;当断线重连时,从节点会将这个runid发送给主节点;主节点根据runid判断能否进行全量复制:
如果从节点保存的runid与主节点现在的runid不同,说明从节点在断线前同步的Redis节点并不是当前的主节点,进行全量复制。
Redis集群模式的工作原理说一下?在集群模式下,key是如何寻址的?寻址都有哪些算法?了解一致性hash吗?
寻址都有哪些算法
hash 算法
根据key的hash值然后取模节点数 , hash(key)%节点数。
缺点:当节点宕机或者新增,会导致节点数变换,所有数据都要重新计算。
redis cluster 的 hash slot 算法
上面已讲
一致性 hash 算法
一致性hash算法通过一个叫作一致性hash环的数据结构实现,环的整数分布范围是( 0 , 1 , 2 , 3 … 2^32-1 ) ,如下图:
假设现在我们有4个对象,分别为o1,o2,o3,o4,使用hash函数计算这4个对象的hash值(范围为0 ~ 2^32-1):
hash(o1) = m1
hash(o2) = m2
hash(o3) = m3
hash(o4) = m4
将m1,m2,m3,m4落在hash环上:
假设我们集群中有 c1,c2,c3三台机器,分别用其ip地址取hash:
hash(c1的ip) = t1
hash(c2的ip) = t2
hash(c3的ip) = t3
将t1,t2,t3落在hash环上:
在hash环上顺时针查找距离这个对象的hash值最近的机器,即是这个对象所属的机器。如上图所示:
新增机器情况
如上图,我们新增了c4机器,计算出落在了hash环上的t4位置,现在只需重组o4对象重新落在c4机器上就ok了,其他对象仍在原有机器上不动。
宕机情况
如上图,我们c1宕机了,则o2需要重组到c3机器上,其他对象仍在原有机器上。
Hash环的数据倾斜问题
一致性Hash算法在服务节点太少时,容易因为节点分部不均匀而造成数据倾斜(被缓存的对象大部分集中缓存在某一台服务器上)问题,例如系统中只有两台服务器,其环分布如下:
此时必然造成大量数据集中到Node A上,而只有极少量会定位到Node B上。为了解决这种数据倾斜问题,一致性Hash算法引入了虚拟节点机制。
虚拟节点
就是对真实的机器映射出多个虚拟节点,那么在hash环上就仿佛有很多个机器节点。具体做法可以在服务器IP或主机名的后面增加编号来实现。
例如上面的情况,可以为每台服务器计算三个虚拟节点,于是可以分别计算 “Node A#1”、“Node A#2”、“Node A#3”、“Node B#1”、“Node B#2”、“Node B#3”的哈希值,于是形成六个虚拟节点:
同时数据定位算法不变,只是多了一步虚拟节点到实际节点的映射,例如定位到“Node A#1”、“Node A#2”、“Node A#3”三个虚拟节点的数据均定位到Node A上。这样就解决了服务节点少时数据倾斜的问题。在实际应用中,通常将虚拟节点数设置为32甚至更大,因此即使很少的服务节点也能做到相对均匀的数据分布。
Java架构师修炼githubs.xyz