Redis之路系列(4)众人拾柴火焰高

4 集群篇---众人拾柴火焰高

复制

基本概述

Redis支持复制的功能,以实现当一台服务器的数据更新后,自动将新的数据异步同步到其它数据库。

Redis复制实现中,把数据库分为主数据库master和从数据库slave ,主数据库可以进行读写操作,从数据库默认是只读的,当主数据库数据变化的时候,会自动同步给从数据库。

主从复制的结构支持一个Mater带多个Slave,也可以Slave带Slave模式

复制的好处:

  • 可以实现读写分离
  • 在主数据崩溃时可以实现数据恢复
  • 可进行水平扩容支撑高并发
基本配置
  • 配置原则 :主数据库不做配置,从数据库中设置 replicaof 主数据库ip 主数据库port

具体的配置请查阅附录一

基本原理
  • 全量复制
  • slave启动时,会向master发送psync请求,如果这是slave重新连 接master,那么master仅仅会复制给slave缺少的数据; 如果是第 一次连接master,那么会触发一次全量复制

  • 主数据库接到psync请求后,如果是全量复制,会在后台保存快照 ,就是实现RDB持久化,并将保存快照期间接收到的命令缓存起来

  • 快照完成后,主数据库会将快照文件和所有缓存的命令发送给从数据库

  • 从数据库接收后,会载入快照文件并执行缓存的命令,从而完成复 制的初始化

  • 增量复制

  • 如果是重新连接,Master会检查backlog里面的offset, master和 slave都会保存一个复制的offset还有一个master id,offset是保 存在backlog中的。Master只会把已经复制的offset后面的数据复 制给Slave,类似断点续传
  • 在数据库使用阶段,主数据库会自动把每次收到的写命令同步到从 服务器
  • Slave在复制的时候,不会阻塞Master的正常工作;也不会阻塞对 自己的查询操作,它会用旧的数据集来提供服务; 但是复制完成的 时候,需要删除旧数据集,加载新数据集,这个时候就会暂停对外 服务了

PS:Master一定要持久化

  • 乐观复制策略:Redis采用乐观复制的策略,容忍在一定时间内主从数据库 的内容不同,当然最终的数据会是一样的。

这个策略保证了性能, 在复制的时候,主数据库并不阻塞,照样处理客户端的请求。 Redis提供了配置来限制只有当数据库至少同步给指定数量 的从数据库时,主数据库才可写,否则返回错误。配置是:min-replicas-to-write、min-replicas-max-lag

  • 无硬盘复制:Master直接创建一个子进程,来生成RDB文件的内容,并通 过网络直接传送给Slave,也就是RDB文件不保存到磁盘上。
数据丢失解决方案
  • 异步复制导致丢失:master宕机,在主备切换的过程中,可能会导致数据丢失。
  • 主从脑裂导致丢失:网络原因导致master短时间脱离集群,集群产生了新master,但客户端仍然往旧mater写数据,网络恢复后,旧master变成slave与新mater同步导致数据丢失。

解决方案:配置min-replicas-to-write和min-replicas-max-lag两个参数,比如: min-replicas-to-write 1 、 min-replicas-max-lag 10。

要求至少有1个slave,数据复制和同步的延迟不能超过10秒。

一旦所有的slave,数据复制和同步的延迟都超过了10秒钟, 那么这个时候,master就不会再接收任何请求了。

脑裂情况下,一个master跟其它slave丢了连接,那么这两个配置可以确保,如果不能继续给指定数量的slave发送数据,而且slave 超过10秒没有给自己应答消息,那么就直接拒绝客户端的写请求。

哨兵

基本概述

哨兵是Redis 复制集集群的重要组件,它的主要作用有:

  • 集群监控:监控主从数据库运行是否正常
  • 故障转移:当主数据库出现故障时,自动将从数据库转换成为主数据库
  • 配置中心:客户端通过连接哨兵来获得当前Redis服务的主节点地址
  • 消息通知:哨兵可以将故障转移的结果发送给客户端

开启哨兵功能需要建立一个个sentinel.conf文件,里面设置要监控的主数据库的名字,比如:sentinel monitor 监控的主数据库的名字 127.0.0.1 6380 1

其中数字1表示的是哨兵判断主节点是否法神故障的最低票数,这个文件在运行期间会被sentinel动态进行更改,可以同时监控多个主数据库,一行一个配置即可,详细的配置参考附录二。

哨兵集群在整个主从集群中的功能关系结构图如下:

主从节点存放数据,而哨兵节点用来自动监控和维护集群,不存放数据

每个哨兵节点维护了3个定时任务:

  • 1 哨兵向master-slave节点发送info命令来获取复制集的结构
  • 2 哨兵之间通过pub/sub系统来互相感知装态和信息
  • 3 哨兵向master-slave节点发送ping命令来进行心跳检测
基本原理
  • SDown:主观下线,在心跳检测的定时任务中,如果其它节点超过一定时间没有回复, 哨兵节点就会将其进行主观下线。也就是说,主观下线的意思是一个哨兵节点"主观地"判断某个节点下线

  • ODown:客观下线,哨兵节点在对主节点进行主观下线后,会通过sentinel is-master-down-by-addr命令询问其它哨兵节点该主节点的状态;如果判断主节点下线的哨兵数量达到一定数值,则对该主节点进行客观下线。

哨兵四大基本原理:自动发现、选举领导、故障转移、配置传播

  • 自动发现
    哨兵互相之间的相互自动发现,是通过redis的pub/sub系统实现的 ,每个哨兵都会往__sentinel__:hello这个channel里发送一个消息,这时 候所有其他哨兵都可以消费到这个消息,并感知到其他的哨兵的存在。

每隔两秒钟,每个哨兵都会往自己监控的某个master+slave对应的 sentinel:hello channel里发送一个消息,内容是自己的host、ip和 runid还有对这个master的监控配置。

每个哨兵也会去监听自己监控的每个master+slaves对应的 sentinel:hello channel,然后去感知到同样在监听这个 master+slave的其他哨兵的存在。

每个哨兵跟其他哨兵交换对master的监控配置,互相进行监控配置 的同步

  • 选举领导:选举出领导者哨兵

哨兵选举涉及两个参数:quorum(确认客观下线的最少的哨兵数量),majority(授权进行主从切换的最少的哨兵数量)

每次一个哨兵要做主备切换,首先需要quorum数量的哨兵认为master客观下线,然后选举出一个哨兵来做切换,这个哨兵还需要得到majority哨兵的授权,才能正式执行切换

当主节点被判断客观下线以后,各个哨兵节点会进行协商, 选举出一个领导者哨兵节点,并由该领导者节点对其进行故障转移操作。

监视该主节点的所有哨兵都有可能被选为领导者,选举使用的算法是Raft算法;Raft算法的基本思路是先到先得:即在一轮选举中,哨兵A向B发送成为领导者的申请,如果B没有同意过其他哨兵,则会同意A成为领导者

  • 故障转移:选举出的领导者哨兵,开始进行故障转移操作,选举出新mater节点,大体可以分为3个步骤

1 在从节点中选择新的主节点:选择的原则是,首先过滤掉不健康的从节点; 然后选择优先级最高的从节点(由slave-priority指定);如果优先级无法区分,则选择复制offset最大的从节点;如果仍无法区分,则选择runid最小的从节点

2 更新主从状态:通过slaveof no one命令,让选出来的从节点成为主节点; 并通过slaveof命令让其他节点成为其从节点

3 将已经下线的主节点设置为新的主节点的从节点,当其重新上线后,它会成为新的主节点的从节点

  • 配置传播
    新的master选出过后,执行切换的那个哨兵,会从要切换到的新master那里得到一个configuration epoch,这就是一个 version号,每次切换的version号都必须是唯一的。

如果第一个选举出的哨兵切换失败了,那么其他哨兵,会等待failover-timeout时间,然后接替继续执行切换,此时会重新获取一个新的configuration epoch,作为新的version号。

哨兵完成master切换之后,会在自己本地更新生成最新的master配置,然后同步给其他的哨兵,就是通过之前说的pub/sub 消息机制。

这时version号就很重要了,因为各种消息都是通过一个 channel去发布和监听的,所以一个哨兵完成一次master的切换之 后,新的master配置是跟着新的version号的。

其他的哨兵都是根据版本号的大小来更新自己的master配置的。

使用建议
  • 哨兵节点的数量应为多个,哨兵本身应该集群,保证高可用
  • 哨兵节点的数量应该是奇数
  • 各个哨兵节点的配置应一致

另外如果哨兵节点部署在Docker等容器里面,尤其要注意端口的正确映射,还需要知道哨兵集群+主从复制,并不能保证数据零丢失

分片

由于复制中,每个数据库都是拥有完整的数据,因此复制的 总数据存储量,受限于内存最小的数据库节点,如果数据量过大, 复制就无能为力了。

这个时候就需要用到分片技术,分片是将数据拆分到多个Redis实例的过程,这样每个Redis实例将只包含完整数据的一部分。

常见的分片方式有:按范围分片和哈希分片

分片实现方式
  • 在客户端进行分片
  • 通过代理来进行分片,比如:Twemproxy
  • 查询路由:就是发送查询到一个随机实例,这个实例会保证转发你的查询到正确的节点,Redis集群在客户端的帮助下,实现了查询路由的一种混合形式,请求不是直接从Redis实例转发到另一个, 而是客户端收到重定向到正确的节点(客户端启动时,加上-c参数)
  • 在服务器端进行分片, Redis采用哈希槽(hash slot)的方式在服务器端进行分片:Redis集群有16384个哈希槽,使用键的CRC16 编码对16384取模来计算一个键所属的哈希槽
分片缺点
  • 不支持涉及多键的操作,如mget,如果所操作的键都在同一个节点 ,就正常执行,否则会提示错误
  • 分片的粒度是键,因此每个键对应的值不要太大
  • 数据备份会比较麻烦,备份数据时你需要聚合多个实例和主机的持 久化文件
  • 扩容的处理比较麻烦
  • 故障恢复的处理会比较麻烦,可能需要重新梳理Master和Slave的 关系,并调整每个复制集里面的数据

结论:尽量避免去使用分片概念

集群架构

基本概念

由于数据量过大,单个复制集难以承担,因此需要对多个复制集进行集群,形成水平扩展,每个复制集只负责存储整个数据集的一部分,这就是Redis的集群。

分片可以模拟实现集群效果,但是有非常多的缺点,已经不推荐使用

现在Redis支持集群,在不降低性能的情况下,还提供了网络分区后的 可访问性和支持对主数据库故障的恢复。

  • redis集群支持多个Master,每个Master可以挂载多个Slave,便于支持读写分离
  • 集群Cluster内置了高可用的支持,无需再去使用哨兵的功能
  • 集群中所有节点彼此互相连接,内部使用二进制协议(gossip)来进行优化传输速度和带宽
  • 客户端与任一节点直接连接即可访问集群数据
  • 集群负责把插槽分配到各个物理服务节点,由集群来负责维护节点、插槽、数据之间关系

使用集群后,都只能使用默认的0号数据库

每个Redis集群节点需要两个TCP连接打开,正常的TCP端口用来服 务客户端,例如6379,加10000的端口用作数据端口,必须保证防火墙打开这两个端口

Redis集群不保证强一致性,这意味着在特定的条件下,Redis集群 可能会丢掉一些被系统收到的写入请求命令。

手工创建集群

集群的最低标准6个redis实例:3个master节点,每个master带一个slave节点

集群创建的步骤大致 如下:

  • 1 集群配置:将每个数据库的cluster-enabled配置选项打开,然 后再修改如下内容:pidfile、port、logfile、dbfilename、 cluster-config-file
  • 2 启动数据库:分别启动这些Redis数据库,可以用info cluster查看信息
  • 3 连接节点:使用cluster meet,把所有的数据库都放到一个集群中,可以通过cluster info ,或者cluster nodes 查看信息
  • 4 设置slave::设置部分数据库为slave,使用cluster replicate
  • 5 分配插槽:使用cluster addSlots,这个命令目前只能一个一个加,如果要加区间的话,就得客户端编写代码来循环添加。有个实用的技巧:把所有的Redis停下来,然后直接修改node-xxx.conf 的配置文件,只需要配置master的数据库就可以,然后再重启数据库。分配完记得用使用cluster slots命令检查下

最后,通过cluster info查看集群信息,如果显示ok,那就可以使用了

插槽与预分区

插槽

插槽是Redis对Key进行分片的单元。在Redis的集群实现中 ,内置了数据自动分片机制,集群内部会将所有的key映射到16384个插槽中,集群中的每个数据库实例负责其中部分的插槽的读写。

键与插槽的关系:Redis会将key的有效部分,使用CRC16算法计算出散列值, 然后对16384取余数,从而把key分配到插槽中。

键名的有效部分规则是:如果键名包含{},那么有效部分就是{}中的值;否则就是取整个键名

可以看成是集群数据的逻辑划分,也是redis内存的逻辑划分

移动已分配的插槽,这个稍微麻烦点,尤其是有了数据过后,假设要迁移123号插槽从A 到B,大致步骤如下:

  • 1:在B上执行cluster setslot 123 importing A
  • 2:在A上执行cluster setslot 123 migrating B
  • 3:在A上执行cluster getkeysinslot 123 要返回的数量
  • 4:对上一步获取的每个键执行migrate命令,将其从A迁移到B
  • 5:在集群中每个服务器上执行cluster setslot 123 node B (集群其实会自动同步,不过有延迟)

避免键的临时丢失:上面迁移方案中的前两步就是用来避免在移动已分配插槽过 程中,键的临时丢失问题的,大致思路如下

1 当前两步执行完成后,如果客户端向A请求插槽123中的键时,如果 键还未被转移,A将处理请求

2 如果键已经转移,则返回,把新的地址告诉客户端,客户端将发起 新的请求以获取数据

当客户端向某个数据库发起请求时,如果键不在这个数据库里面,将会返回一个move重定向的请求,里面包含新的地址,客户端收到这个信息后,需要重新发起请求到新的地址去获取数据。

当然,大部分的Redis客户端都会自动去重定向,也就是这个过程对开发人员是透明的。redis-cli也支持自动重定向,只需要在启动时加入 -c 的 参数。

预分区

为了实现在线动态扩容和数据分区,Redis的作者提出了预分区的方案,实际就是在同 一台机器上部署多个Redis实例,当容量不够时将多个实例拆分到不同的机器上,这样就达到了扩容的效果。

  • 1:在新机器上启动好对应端口的Redis实例
  • 2:配置新端口为待迁移端口的从库
  • 3:待复制完成,与主库完成同步后,切换所有客户端配置到新的从库的端口
  • 4:配置从库为新的主库
  • 5:移除老的端口实例
  • 6:重复上述过程把要迁移的数据库转移到指定服务器上

以上拆分流程是Redis作者提出的一个平滑迁移的过程,不过该拆分方法还是很依赖Redis本身的 复制功能的,如果主库快照数据文件过大,这个复制的过程也会很久,同时会给主库带来压力。

集群故障恢复

故障判定
  • 集群中每个节点都会定期向其他节点发出ping命令,如果没有收到回复,就 认为该节点为疑似下线,然后在集群中传播该信息
  • 当集群中的某个节点,收到半数以上认为某节点已下线的信息,就会真的标 记该节点为已下线,并在集群中传播该信息
  • 如果已下线的节点是master节点,那就意味着一部分插槽无法写入了
  • 如果集群任意master挂掉,且当前master没有slave,集群进入fail状态
  • 如果集群超过半数以上master挂掉,无论是否有slave,集群进入fail状态
  • 当集群不可用时,所有对集群的操作做都不可用,客户端会收到The cluster is down错误信息
故障恢复

发现某个master下线后,集群会进行故障恢复操作,来将一个slave 变成master,基于Raft算法,大致步骤如下:

  • 1:某个slave向集群中每个节点发送请求,要求选举自己为master
  • 2:如果收到请求的节点没有选举过其他slave,会同意
  • 3:当集群中有超过节点数一半的节点同意该slave的请求,则该Slave选举成功
  • 4:如果有多个slave同时参选,可能会出现没有任何slave当选的情况,将会等 待一个随机时间,再次发出选举请求
  • 5:选举成功后,slave会通过 slaveof no one命令把自己变成master

如果故障后还想集群继续工作,可设置cluster-require-full-coverage为 no,默认yes。

另外要弄清楚master挂掉了,重启还可以加入集群,只是变成Slave;但挂掉的slave重启,如果对应的master变化了,是不能加入集群 的,除非修改它们的配置文件,将其原master指向新master。

只要主从关系建立,就会触发主和该从采用save方式持久化数据, 不论你是否禁止save。

在集群中,如果默认主从关系的主挂了并立即重启,如果主没有做持久化,数据会完全丢失,从而从的数据也被清空。

建议恢复机制交给集群自己去处理,不要使用脚本来控制master的启停,操作不当会导致数据完全丢失

相关推荐
FIN技术铺2 小时前
Redis集群模式之Redis Sentinel vs. Redis Cluster
数据库·redis·sentinel
CodingBrother4 小时前
MySQL 中的 `IN`、`EXISTS` 区别与性能分析
数据库·mysql
代码小鑫4 小时前
A027-基于Spring Boot的农事管理系统
java·开发语言·数据库·spring boot·后端·毕业设计
小小不董4 小时前
Oracle OCP认证考试考点详解082系列16
linux·运维·服务器·数据库·oracle·dba
甄臻9244 小时前
Windows下mysql数据库备份策略
数据库·mysql
内蒙深海大鲨鱼4 小时前
qt之ui开发
数据库·qt·ui
Wlq04155 小时前
分布式技术缓存技术
分布式·缓存
不爱学习的YY酱5 小时前
【计网不挂科】计算机网络第一章< 概述 >习题库(含答案)
java·数据库·计算机网络
这样の我5 小时前
hbase集成phoenix
大数据·数据库·hbase
安静读书6 小时前
MongoDB 详解:深入理解与探索
数据库·mongodb