redis集群

怎么实现Redis的高可用:Redis 实现高可用有三种部署模式:主从模式,哨兵模式,集群模式。

  • 主从模式
  • 哨兵模式
    • 由一个或多个Sentinel(哨兵)实例组成的Sentinel系统,它可以监视所有的Redis主节点和从节点,如果被监视的主节点进入下线状态时,会自动将下线主服务器属下的某个从节点升级为新的主节点
    • 但是,一个哨兵进程对Redis节点进行监控,有可能出现单点故障,因此,可以使用多个哨兵来进行监控Redis节点,并且各个哨兵之间还会进行监控。
    • 哨兵模式简单来说就三个作用:
      • 发送命令,等待Redis服务器(包括主服务器和从服务器)返回监控其运行状态;
      • 哨兵监测到主节点宕机,会自动将从节点切换成主节点,然后通过发布订阅模式通知其他的从节点,修改配置文件,让它们切换主机;
      • 哨兵之间还会相互监控,从而达到高可用。
  • 集群模式
    • 哨兵模式基于主从模式,实现读写分离,它还可以自动切换,系统可用性更高。但是它每个节点存储的数据是一样的,浪费内存,并且不好在线扩容。因此,Cluster集群应运而生
    • 集群模式,可以对数据进行分片,也就是说每台Redis节点上存储不同的内容,来解决在线扩容的问题。并且,它也提供复制和故障转移的功能。

redis主从模式

  • 主从模式中,主节点负责读写操作,从节点只负责读操作。从节点的数据来自主节点,原理就是主从复制

  • redis主从数据是异步同步的,当主节点修改数据后立即返回,即使主从网络断开,主节点依然可以对外提供写服务,所以分布式的redis系统满足CAP中的可用性,不满足一致性

  • redis会保证最终一致性,从节点会努力追赶主节点,如果网络断开,主从节点的数据有可能出现大量不一致的情况,但一旦网络恢复,从节点会采用多种策略努力追赶

  • redis的同步支持主从同步和从从同步,从从同步是为了减轻主节点的同步负担

  • 同步方式分为增量同步和快照同步:

    • 增量同步
      • redis同步是指令流,主节点会把对自己的状态产生修改的指令记录在本地内存buffer中,然后异步将主节点buffer中的指令同步到从节点,从节点一边执行同步过来的指令,一边向主节点反馈自己同步的偏移量
      • redis的复制内存buffer是一个定长的环行数组,如果数组满了,就会从头开始覆盖前面的内容
      • 如果因为网络原因,从节点和主节点无法在短时间内同步,主节点中还未来得及同步的指令有可能被覆盖掉,这时就需要快照同步了
    • 快照同步:
      • 也就是全量复制,是一件很消耗资源的操作,首先要在主节点上执行一次bgsave操作,把内存中的数据全部快照到磁盘中,然后把快照中的内容全部传送到从节点
      • 从节点接受完毕后,先把内存中数据全部清空,然后执行一次全量加载,加载完毕后通知主节点继续进行增量同步
      • 在进行全量复制的时候,主节点的复制buffer还在不停地向前移动,如果全量复制的时间过长,或者复制buffer过小,都会导致同步期间的增量指令被覆盖,这样会再一次发起全量复制,很有可能会陷入死循环
      • 所以要配置一个合适的buffer参数
    • 在从节点刚刚加入到集群中时,也需要先进行一次全量复制,在继续进行增量复制
  • 全量复制时,会进行很耗时的IO操作,对系统带来较大负担,所以在2.8版本开始,redis支持无盘复制,也就是直接通过套接字将快照内容发送到从节点

  • redis的复制是异步进行的,3.0版本后使用wait指令可以让异步复制变为同步复制,确保系统的强一致性

    • wait有两个参数,一个是从节点的数量N,一个是以毫秒为单位的时间T
    • 含义是:最多等待时间T,确保wait指令之前的所有写操作同步到N个节点,如果T为0就是无限等待,如果此时产生了分区,主从同步无法进行就会永远阻塞,使redis丧失可用性

哨兵(sentinel):

  • 哨兵负责持续监控主从节点的健康,当主节点挂掉后,会自动选择一个最优的从节点切换成主节点
  • 客户端连接集群时,会首先连接哨兵,通过哨兵来查询主节点的地址,然后在连接主节点进行数据交互,当主节点发生故障时,客户端会重新向哨兵要最新的主节点地址
  • 如果主节点挂掉了,原本的主从复制就断开了,客户端和损坏的主节点就也断开了,一个从节点被提升为新的主节点,其他从节点开始和新的主机点建立复制关系,客户端也会通过新的主节点进行交互,哨兵会持续监控已经挂断的主节点,等他恢复后,把它作为从节点重新加入集群
  • 因为主从复制是异步的,所以主节点挂掉后,可能会造成部分数据丢失,哨兵不能保证数据完全不丢失,但是可以通过两个选项限制主从复制延迟过大,尽量保证数据少丢失
    • 第一个参数是:min-slaves-to-write,表示主节点至少要有多少个从节点在正常复制,否则就停止对外写服务,失去可用性
    • 第二个参数是:min-slaves-max-lag ,表示多少时间内没有收到从节点的反馈,就认为该从节点同步不正常,单位是秒
  • 哨兵的默认端口是26379,不同于redis的默认端口6379

集群模式

  • 单个redis的内存不宜过大,太大的内存会导致rdb文件过大,进一步导致主从复制时全量复制时间过长,重启恢复也需要更长的时间,而且一个redis是单线程的,cpu的压力也会很大

  • 所以在大数据高并发下,集群方案应运而生:codis、cluster

codis
  • go语言开发的一个代理中间件,使用redis的协议对外提供服务,当客户端向codis发送指令时,codis负责将指令转发到集群后面的实例,使上层应用可以像使用单机的 Redis 一样使用,并将返回结果转回给客户端
  • 当集群空间不足,可以动态的增加redis实例来实现扩容需求
  • codis是无状态的,它只是一个转发代理中间件,因为当个节点能支撑的QPS有限,可以启动多个codis能显著增加整体的QPS,还能起到容灾的作用,每个codis节点都是对等的

codis原理:

  • 默认将所有的key划分为1024个槽位,首先计算客户端传过来的key的hash值,然后对1024进行取模,得到的余数就是对应key的槽位
  • 每个槽位都会映射到后面的多个redis实例,codis会在内存中维护槽位和redis实例的映射关系,如果有多个codis实例的话,就需要一个分布式配置存储数据库来持久化槽位,支持zookeepr和etcd
  • 将codis槽位存储于zookeeper中,并提供一个Danshboard可以用来观察和修改槽位,当槽位发生变化时,codis会监听到变化并重新同步槽位关系,从而实现多个codis共享相同的槽位关系
  • 在使用mget批量获取多个key值时,这些key有可能分布在多个节点上,codis的策略是将key按照所分配的实例打散分组,依次调用每个实例,最后将结果汇总
  • 扩容:
    • 当redis集群增加一个实例的时候,就需要对应的槽位关系进行调整,将一部分槽位划分给新的节点,也就是要把这部分槽位对应的key迁移到新的节点
    • codis增加了SLOTSSCAN指令,可以遍历出槽位下的所有key,然后挨个迁移到新的节点,scan指令是无法避免重复的,自定义的SLOTSSCAN指令也一样,不过这个并不会影响迁移,因为key被迁移之后,在旧的实例就会彻底删除,也就不可能被再次扫描出来了
    • 迁移过程中codis还是会接收新的请求打到正在迁移的槽位上,但是数据是处于新旧两个槽位上的,codis无法判断迁移过程中key究竟在哪个实例中,所以在codis在接收到了位于正在迁移槽位中的key后,会立即强制对当前的单个key进行迁移,迁移完成后,在将请求转发到新的redis实例
    • 并且codis提供了自动均衡功能,会在系统比较空闲的时候,观察每个redis实例对应的槽数量,如果 不平衡,就会自动进行迁移

codis缺点:

  • 因为codis中的所有可以都分散在不同的redis实例中,也不在支持事务了,事务只能在单个redis实例中完成
  • rename操作会很危险,参数的两个key,如果在不同的redis实例中,操作无法正确完成,所以codis有很多不支持的命令
  • 为了支持扩容,单个key对应的value不宜过大,因为迁移的最小单位就是key,官方建议单个集合结构的总字节不要超过1M
  • 因为增加了codis作为中转层,在网络开销上要比单个redis大,整体性能上也比单个redis的性能有所下降
  • codis的集群配置需要增加zookeeper,增加了系统的复杂度

优点是比官方的cluster实现简单很多,把分布式的问题交给第三方,省去了复杂的分布式一致性代码的编写和维护工作

cluster
  • 是redis官方提供的集群化方案,cluster是去中心化的,集群由多个redis节点组成,每个节点负责一部分数据,他们之间通过特殊的二进制协议来交互集群信息
  • redis cluster将所有的数据划分为16384个槽位,每个节点负责一部分槽位,槽位的信息存储在不同的节点中,不需要另外的分布式存储空间
  • 当cluster的客户端来连接集群时,也会得到一份集群的槽位信息,这样客户端要查找某个key时,可以直接定位到目标节点,cluster每个节点会把集群的配置信息持久化到配置文件中,所以必须确保配置文件是可写的,而且尽量不要依靠人工修改配置文件
  • cluster定位槽位同样是通过hash值的取模运算,也允许强制把某个key挂在特定的槽位上
  • 当客户端向一个错误的节点发出指令后,该节点会发现key所在的槽位并不归自己管理,就会向客户端发送一个MOVED 加上 对应key的槽位信息 以及 目标节点ip和端口号的命令,告诉客户端去连接这个节点以获取数据
  • 客户端在收到MOVED指令后,要立即纠正本地的槽位映射表
  • cluster可以为每个主节点设置若干个从节点,当主节点发生故障时某个从节点会提升为主节点,如果某个主节点没有从节点,当他发生故障时,集群就会处于完全不可用,redis也提供了对应的参数,可以允许部分节点发生故障,其他节点还可以继续提供访问

cluster的迁移:

  • redis迁移的单位是槽,当一个槽正在迁移时,这个槽就处于中间状态
  • 迁移时会一次性获取源节点槽位的所有key列表,在对key挨个进行迁移,原节点通过对key执行dump指令获取序列化内容,发送给目标节点后,目标节点在反序列化,就可以把内容恢复到目标节点内存,最后在删除原节点的key,就完成了一个key的迁移
  • 这个迁移过程是同步的,会则阻塞原节点,如果在迁移过程中出现网络故障,整个槽只迁移了一半,这时两个节点依旧处于中间过渡状态,等待网络连接,会重新提示进行迁移
  • 如果key的内容很大,会导致原节点和目标节点的卡顿,影响集群的稳定,所以应该尽可能的避免产生很大的key
  • 在迁移过程中,当客户端的访问请求到来时,
    • 因为数据存在于新旧两个节点,客户端会先尝试访问旧节点,如果数据还在旧节点,那么旧节点就正常处理,
    • 如果对应的数据不在旧节点里,那么数据有可能在新节点,也有可能不存在,此时会向客户端返回一个重定向指令,客户端收到之后,会去目标节点执行ASKING指令,然后在目标节点在执行原本的操作指令
    • 因为在没有迁移完成前,这个槽位还是不归新节点管理的,此时这个槽的指令目标节点是不认的,它会向客户端返回重定向到原节点的指令, 这样就形成了重定向循环,ASKING指令就是打开目标节点的选项,告诉他下一条指令要当作自己的槽位来处理

可能下线:

  • 因为cluster是去中心化的,一个节点认为某个节点失联了,并不代表所有节点都认为它失联了,集群还要经过一次协商,当大多数都认为该节点失联了,集群才认为这个节点需要进行主从切换来容错
  • redis节点采用Gossip协议来广播自己的状态以改变对整个集群的认知,当集群的大多数都认为节点失联,就会强迫其他节点也接收该节点下线的事实,并立即对失联节点进行主从切换

槽位迁移

  • 客户端保存了槽位和节点的映射关系表,但是它需要及时得到更新,才可以正常的将某条指令发送到正确的节点中
  • MOVED指令就是用来纠正槽位的,当指令发送到了错误的节点,就会将MOVED指令和目标节点的地址返回给客户端,这时客户端就会 刷新自己的槽位关系表,然后重试指令
  • ASKING是用于临时纠正槽位的,客户端不会刷新槽位信息,只是临时纠正槽位,对后续的请求没有影响
  • MOVED和ASKING都是重试指令,客户端会因为这两个指令多重试一次,但是也有可能会重试多次,所以需要在代码层面设定一个最大重试次数,超过这个次数,需要向业务层抛出异常
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值