reids笔记4 集群

主从机制

基本原理

CAP原理:

  • Consistent:一致性
  • Availability:可用性
  • Partition tolerance:分区容忍性

网络分区:分布式节点网络断开的场景。

CAP基本原理是:当网络分区发生时,不能同时保证一致性和可用性。

redis支持主从同步和从从同步:

replicate
replicate
replicate
replicate
master
slave
slave
slave
slave

redis的主从数据是异步的,分布式的redis不满足一致性;主从网络断开的时候,主节点仍然可以提供服务,因此满足可用性。redis满足最终一致性,从节点会努力追赶主节点,最终两者状态一致。

同步机制

增量同步

  • 主节点有一个环形队列,用于存储改变内存状态的值
  • 主节点会在队列中不断写入改变自己状态的指令
  • 指令不断发送给从节点进行同步

注意:如果写入速度超过读取速度,则环形队列值会被新的数据覆盖

快照同步

  • 主节点进行bgsave,输出存储到磁盘
  • 发送快照到对应的从节点
  • 从节点继续经过增量同步来同步数据

这种方式比较常用,新节点加入redis集群时,也是全量加载一次,然后增量加载数据

增量和快照同步时,数据都经过磁盘了

无盘复制:直接遍历内存数据,然后发送给从节点,速度快但是精度太差。

快照同步会影响正在进行AOF的fsync操作,发生快照同步会推迟fsync的过程,影响主节点的效率。

wait指令会让redis的异步复制变成同步复制,但是阻塞服务,而且如果网络分区,则redis节点将永远阻塞。

哨兵机制

问题背景
主从机制的缺陷:

  • 主从集群依赖master的活性,如果master崩溃,则从节点无法同步数据
  • 集群没有选举机制,master崩溃后,无法确定新的主节点

问题的本质原因:缺少master奔溃后的选举很同步机制

解决方案:基本思路是,引入哨兵观测master健康状态,如果检测到master崩溃,则确认新的master,并使用新的master同步集群数据。

注意,哨兵是配置提供者,而不是代理

引入哨兵后客户端处理方式

  • 遍历哨兵集群,选择一个可用哨兵服务器。哨兵服务器数据是共享同步的
  • 客户端从哨兵获取redis集群的master的地址,之后与master直接通信
  • 客户端连接不上master后,主动请求哨兵,获取新的地址
  • master其它原因出故障,但是客户端可以与master通信时;哨兵会主动通知客户端更换

详细解决方案:
定时监控是解决问题的核心。基本步骤如下:

  • 每隔10s,向主节点发送info命令,获取集群的拓扑结构。不需要知道从节点的地址,主节点会把信息都发来。主节点故障时,利用之前的info的信息,从从节点中选择一个获取集群信息。
  • 哨兵节点订阅__sentinel__:hello频道,获取其它哨兵对集群节点的判断,也获取其它哨兵信息;同时也发送自己对集群节点的判断。
  • 每隔1s,哨兵向主节点、从节点和其它哨兵节点做心跳检测
  • 对某个节点,如果最后一次收到的回复的时间超过配置的时间,则判定为主观下线;如果master在客观下线之前回复了消息,则恢复正常状态。
  • 足够多的哨兵认为master下线后,则master为客观下线。
  • master客观下线后,哨兵集群选出哨兵的master,让哨兵master在redis的slave节点选出主节点,然后指定其它节点向该节点复制信息。

参考资料:

  • https://zhuanlan.zhihu.com/p/44474652
  • https://juejin.im/post/5c1b482d6fb9a049dd803f55
  • https://hellokangning.github.io/zh/post/redis-sentinel-client-connection/
min-slaves-to-write 1  # 至少有一个从节点在写,否则停止服务
min-slaves-max-lag 10  # 10s内没有收到从节点的反馈,则认为是异常,配合上面那个

Redis Cluster 集群

Redis Cluster把所有的数据分为16384个槽位,槽位的信息存储在每个节点中,不需要其它的分布式存储空间空间来存储节点。
客户端请求redis集群的两种方式:

  • 简单方式,直接把请求Key发送到连接的任一个客户端上。如果key命中了则进行读写,否则该服务器会回发MOVE指令和对应的服务器地址x,此时对x服务器操作
  • Smart客户端:保留服务器的映射表,然后客户端自己计算对应的服务器地址,并发送。可以定时更新表,也可以当请求失败时更新。

redis的slot和服务的映射关系:
16384个槽有自己对应的映射关系,然后迁移的时候,只需要移动对应槽中的数据即可。使用slot=CRC16(key)/16384来计算对应的key属于哪个槽:

redis-trib是redis的迁移工具,迁移单位是槽,迁移的槽处于中间过渡状态。

单个key的迁移过程:

  • 对当前key执行dump指令序列化
  • 通过“客户端”向目标节点发送数据
  • restore指令携带序列化的内容作为参数
  • 目标节点反序列化参数
  • 目标节点返回“客户端”OK内容
  • 当前节点删除key,完成迁移

迁移的过程是同步的,执行restore指令到删除key的时候,主线程是阻塞的。如果迁移过程中出现故障,则下次重新连接工具时,会自动迁移。

集群环境下,避免产生大key,否则迁移会发生阻塞,影响线上服务。

迁移过程中,客户端访问流程:

  • 先访问旧节点,数据存在则直接返回
  • 旧节点数据不在,两种可能:
    • 数据在新节点
    • 数据根本不存在

对于第二种情况,旧节点返回一个-ASK targetNoodeAddr 的重定向指令,客户端收到该指令后,去目标节点执行一个不带参数的ASKING指令,然后再在目标节点执行原来的查询指令。

迁移没完成的时候,新节点无法管理有关的key,如果此时直接发送查询指令,新节点不认可该指令,会向客户端发送-MOVE指令,让客户端再去源节点查询,如此会形成重定向循环ASKING指令是告诉目标节点,必须执行下面的指令。因此迁移影响效率,原来只需要一个ttl,现在需要3个ttl了。

Redis Cluster提供了cluster-node-timeout,表示节点持续对应的时间失联时,才认定节点出现故障,并进行主从切换,如果没有该参数,则网络抖动可能引起主从频繁切换。

Redis Cluster是去中心化的,不支持事务,mget等命令会慢,因为这是拆分到多个集群执行的。rename等方法也不是原子的,会从源节点转移到目标节点。

槽位变化感知:
MOVED指令修正槽位,如果客户端请求的数据不在当前节点,则当前节点返回给客户端MOVE指令,并带有目标节点的地址,此时客户端会刷新自己的映射表。

ASKING指令临时修正槽位,发生再迁移时候,返回给客户端asking error。上面提到了,客户端不会刷新映射表。

2次重试:收到MOVED指令,去新节点时,新节点正在迁移,返回给客户端ASKING的命令了。一般有多次重试的限制次数,超过抛出异常。

集群变更感知:

  1. 目标节点挂掉,客户端抛出ConnectionError,立刻随机挑选一个节点重试,重试的节点通过MOVED指令通知槽位被分配到了新的节点地址。
  2. 运维手动更改集群信息,主节点切换到其它节点,并把旧的节点移除出集群。在旧节点的指令会获取ClusterDown错误,并通知不可用。客户端关闭所有的连接,清空映射表,向上层报错。下一条指令来时,会重新初始化信息。

集群的高可用性
集群中的每个节点,增加至少一个slave节点作为备份。当某个节点的master不可用时,添加对应的slave作为master节点即可。

参考文档

  • https://www.javazhiyin.com/22957.html
  • https://www.infoq.cn/article/sIRPs21lbMvDAJtqQRJE
  • https://zhuanlan.zhihu.com/p/72056688
  • https://medium.com/@pubuduboteju95/deep-dive-into-redis-clustering-1d71484578a9 (推荐)

总结

  • redis主从:主从是为了备份数据,意外情况下可快速恢复
  • redis哨兵:为了保证集群的可用性
  • redis集群:解决单机容量不够的问题
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章