Sentinel哨兵模式
哨兵的作用
- 监控节点状态
- 故障转移
- 通知
监控节点状态
**Sentinel**
会**每秒钟**
给**所有的redis实例**
发送一个**ping**
命令,如果redis实例返回**pong**
,则说明该redis在**正常提供服务**
**Sentinel**
可能会由于**网络或其他的原因导致命令**
没有成功发送或没有成功接收到pong返回值,因此需要引入多个哨兵,两个哨兵当一个成功返回另一个没有时也无法判断,因此Redis主从中**最少设立三个哨兵**
- 当单个
**Sentinel**
没有收到redis的请求时,此时该**Sentinel**
则认为该redis为**客观下线**
。当集群中**客观下线**
的数量达到阈值(**quorum**
)此时则会认为该redis实例**主观下线**
故障转移
- 当
**Sentinel**
集群发现某个节点已经**主观下线**
,此时会从slave节点中选取出一个作为新的Master节点 - 会根据 slave节点和master节点
**断开的时间长短**
,**优先级**
,**offset进度**
等条件进行判断从而选举出新的Master节点 - 在选举出新的Master节点后,
**Sentinel**
集群会对该Slave节点发送 master命令,使该**slave**
节点变为**Master**
- 同时对其他的
**所有Slave节点**
发送命令,使其他所有的slave节点变为Master的slave节点 - 此时其他的slave都会重新对该Master节点进行
**全量同步**
- 最后哨兵会将原故障的Master节点设定为
**新Master节点的slave**
,在节点重新恢复服务后会重新进行数据的同步
通知
- 当出现故障转移后,哨兵会向
**redistemplate**
发送通知,使得可以即使的进行主从库的切换
Sentinel脑裂问题
- 此时如果出现有
**Master节点和哨兵与集群断开连接**
的情况,即出现了**脑裂**
情况。即客户端仍然在对原Master节点进行写操作,集群中又选举出了新的Master,此时则会存在两个Master节点。 - 此时客户端仍然在对该节点进行读写操作,而此时会出现问题,因为
**读到的数据可能是旧的数据**
,并且**写入的数据也会丢失**
,因为该Master节点在断开连接后,会从剩余的Slave节点中重新选举出新的Master节点 - 此时原先断开的Master节点则会作为Slave节点重新连接,因此先前客户端对原Master节点的写操作可能会丢失
- 因此则需要进行配置,防止在脑裂情况下出现数据的问题,可以配置限制参数,
**写入Master节点最少要写入的slave节点数**
,使得在发生脑裂的情况下,使得Redis节点**不接受写**
操作,从而保证数据问题。但这样会**降低Redis的可用性**
。
//Master节点至少写入的salve数量 如果为0则不开启此功能
//当有3个节点时,可以设置为1,当一个主节点断开连接后,此时该节点不会接受新的写请求
min-replicas-to-write 1
//Master节点在经过这段时间后获取到响应 就会认为该节点失联 停止接受新的写入命令请求
min-replicas-max-tag 10
Cluster集群模式
为什么需要Cluster
**Sentinel**
提供了故障转移和读写分离的能力,提高了Redis的**读性能**
。但是如果有**大量写操作**
时,只有主库处理写操作的能力不足。同时在**Sentinel**
架构中,只能存储**一个节点**
的数据,在数据量较大的情况下进行数据的存储是不够的。因此需要Cluster支持节点的**动态扩容**
,从而环节数据的**存储压力**
以及**写操作压力**
。
Cluster基础架构
**Cluster**
架构中最少需要**三个Master节点**
,每个Master节点**最少需要有一个Slave节点**
,因此在Cluster架构中,最少需要有**6个节点**
**Cluster**
中的Slave节点与Sentinel架构中不同的是,Cluster架构中的**Slave节点不对外提供读服务**
,仅用做于对应**Master节点的备份使用**
,在对应Master节点宕机后,会从对应的Slave节点中选举出新的Master节点进行替换。- Cluster节点中会通过
**key的有效部分**
计算出对应的Hash值,在与16384进行取余后,从而最终获取到该key对应的**slot插槽**
。Redis会根据该插槽进行查询,因此在Cluster中连接到任意Master节点最终都能够查询到数据,因为在计算出插槽后会向**对应的Master**
进行请求,如果正在处于**扩容或缩容**
状态,会**返回正确的Master地址**
,客户端再次进行请求,直至真正请求到到插槽所在的Master节点,最终进行数据的返回。
Cluster的节点扩容或缩容
扩容
- 在为Cluster节点扩容时,需要配置 新节点的ip和端口号,和要加入的集群中其中一个节点的ip和端口号,用于通知集群中的其他节点
- 新节点的进入默认为Master节点,但是也可以设置为某个Master节点的Slave节点
- 新的节点加入后不会有数据保存,因为此时新的节点中并不持有slot插槽,因为Redis中数据是与插槽绑定的。需要将其他Master节点的插槽分配到新节点中,才能顺利让该节点中存储数据。
缩容
- 在删除某个节点时,需要先将该节点上的插槽分配给其他Master节点,在一个节点上仍然持有插槽时,无法删除该节点。
Cluster的分片算法
**Cluster**
没有通过**一致性哈希**
进行数据的分配,而是采用了**插槽机制**
- 总共有16384个插槽,分配到不同的Master节点上,多个key会对应一个插槽
- Cluster中Hash的计算函数是Crc16,返回值的范围在0~65535,Hash值的计算是通过该key的有效部分,如果key中
**不存在{}**
,则key的**全部部分都是有效部分**
,如果**存在{}**
,并且其中包含最少一个字符,则**括号内的所有部分为有效部分**
- 在进行操作时,会先使用该key的有效部分计算该key所对应的Hash值,再通过Hash值对16384做取余操作,即得到该key所对应的
**插槽位置**
。 - 在
**客户端**
中保存着**插槽与Master节点的对应关系的缓存**
,通过计算出的插槽位置,程序会向对应的Master节点发送请求,如果插槽位置正确则返回正确的数据。 - 如果插槽的位置不正确,例如遇到
**扩容缩容**
等场景,则该Master节点会**告知客户端正确的地址**
,
为什么Cluster的插槽是16384个
- Cluster的Hash函数返回的范围在
**0~65535**
,理论上可以一直65536个插槽 - 但是因为
**gossip**
进行通信,每个节点都会向**所有Master节点**
发送**ping**
命令进行心跳的判断,ping命令需要发送该节点的**插槽信息**
,并且**Master节点越多**
,需要发送的**心跳请求也就越多**
。都会发送该节点的插槽信息,如果是要65536个插槽,会占用更多的空间,并且**16384已经足够用**
,因为Cluster不会设置过多的主节点 - 因为主节点之间通过gossip协议进行通信,在每个节点之间进行消息的通信包含该节点的插槽信息。插槽信息中包含一个bitmap,其中如果是该节点持有的插槽即设置为1,在消息传递的过程中会进行压缩,bitmap中的1越多也就导致压缩率变低,而如果设置为过大的插槽数,则会导致bitmap压缩率变低,从而增大了网络的开销。
Cluster在扩容缩容期间可以提供服务吗
Cluster针对这个问题提供了两种重定向策略
**ASK**
重定向:**暂时**
重定向,不会更新节点信息,后续请求仍然发送到**旧节点**
**MOVED**
重定向:**永久**
重定向,会更新节点信息,后续请求将会发送到**新节点**
当客户端向将要进行插槽转移的节点发送请求时
- 如果该插槽
**还在Master节点**
内,则直接返回对应的数据 - 如果该插槽已经在
**迁移的过程中**
,但是该key的**数据还保留在该节点**
中,则也会**直接返回**
- 如果该插槽已经在迁移的过程中,并且该key的
**数据也已经被转移到其他节点**
,此时该节点则会向客户端发送**ASK重定向**
并携带上**转移后节点的信息**
,此时会**强制**
客户端向新节点发送**ASKING**
请求。如果可能新节点**还未完全将数据完全导入**
,如果是还没有导入的插槽,则会返回**TRYAGAIN**
。 - 在后续客户端请求该key时,因为ASK请求并不会同步新节点信息,因此在后续请求
**仍然会请求之前的Master节点**
- 直到原Master节点的该插槽
**完全转移到新Master节点**
后,该节点会返回**MOVED**
,此时客户端才会更新对应的节点信息,以后再访问该key时就会访问新的节点。