在Redis服务中,保证服务高可用是非常重要的,除了保证提供正常服务(如主从分离、快速容灾技术),还需要考虑数据容量的扩展、数据安全不会丢失等。
Redis中,实现高可用的技术主要包括:
- 持久化:单机备份问题,最简单的高可用方法;主要作用是数据备份到磁盘,保证不会因为Redis进程退出而数据丢失。
- 主从复制:数据多机热备,高可用Redis的基础;哨兵和集群都是在复制基础上实现高可用的;复制实现了数据的多机备份、读的负载均衡、简单的故障恢复。缺陷:故障恢复无法自动化、写操作无法负载均衡、存储能力受到单机的限制。
- 哨兵:在复制的基础上,哨兵实现了自动化的故障恢复。缺陷:写操作无法负载均衡;存储能力受到单机的限制。
- 集群:通过集群,Redis解决了写的负载均衡,以及存储能力的单机限制、容量扩展等问题,实现了较为完善的高可用方案。
一、Redis主从复制
- 将Master主节数据同步到多个Slave,当Master节点或者其他节点出现宕机后,Redis服务还可以使用。
- Redis为了保持从节点数据一致性,主从之间采用读写分离,可以避免主从都处理写操作,进行加锁等一系列开销。
读操作:主从库都可以执行;
写操作:先在主库执行,再由主库将写操作同步给从库。
主库数据同步从库的方式有两种:全量同步+增量同步
1、全量同步
通常是主从服务器刚刚连接的时候,会先进行全量同步。
- 从库启动,发生给主库PSYN命令进行数据同步请求(命令包含主库的runID和复制进度offset两个位移参数);
- 主库处理PSYNC命令,保存RDB文件发送给从库,发送期间会使用复制缓存区(replication buffer)记录后续的所有写操作;
- 从库收到数据后,会先清空当前数据库,然后加载从主库获取的RDB 文件;
- 主库完成 RDB 文件发送后,也会将保存发送RDB文件期间写操作的replication buffer发给从库,从库再重新执行这些操作。这样一来,主从库就实现同步了。
为了分担主库生成 RDB 文件和传输 RDB 文件压力,提高效率,可以使用 “主 - 从 - 从”模式将主库生成 RDB 和传输 RDB 的压力,以级联的方式分散到从库上。
2、增量同步
一般在全量同步结束后,进行增量同步,或主从库间网络断,再进行数据同步。
基于环形缓冲区repl_backlog_buffer缓存区(复制积压缓冲区)实现,主库会记录自己写到的位置master_repl_offset ,从库则会记录自己已经读到的位置slave_repl_offset, 主库并通过master_repl_offset 和 slave_repl_offset的差值的数据同步到从库。
环形缓冲区:在缓冲区写满后,主库会继续写入,覆盖掉之前写入的操作。如果从库的读取速度比较慢,就有可能导致从库还未读取的操作被主库新写的操作覆盖了,这会导致主从库间的数据不一致,造成主从节点间重新开始执行全量复制。因此需要关注 repl_backlog_size参数,调整合适的缓冲空间大小,避免数据覆盖,主从数据不一致。
主从库间网络断了, 主从库会采用增量复制的方式继续同步,主库会把断连期间收到的写操作命令,写入 replication buffer,同时也会把这些操作命令也写入 repl_backlog_buffer 这个缓冲区,然后主库并通过master_repl_offset 和 slave_repl_offset的差值数据同步到从库。
二、Redis哨兵机制
Redis提供了哨兵机制,哨兵为运行在特殊模式下的 Redis 进程。
哨兵机制是实现主从库自动切换的关键机制,其主要分为三个阶段:
- 监控:哨兵进程会周期性地给所有的主从库发送 PING 命令,检测它们是否仍然在线运行;
- 选主:主库挂了以后,哨兵基于一定规则评分机制选举出一个从库实例作为新的主库 ;
- 通知 :哨兵会将新主库的信息发送给其他从库,让它们和新主库建立连接,并进行数据复制。同时,哨兵会把新主库的信息广播通知给客户端,让它们把请求操作发到新主库上;
哨兵的监控是如何判断主从库下线状态:
- 主观下线: 哨兵进程用 PING 命令检测它自己和主从库的网络连接情况,用来判断实例的状态, 如果当哨兵发现主库或从库对 PING 命令的响应超时了,那么哨兵就会先把它标记为"主观下线"。
- 客观下线:哨兵集群中,基于少数服从多数,多数哨兵都判定主库已"主观下线",数量大于N/2+1(可以自定义设置阙值)则认为主库"客观下线"。
单机哨兵容易误判,误判后主从切换产生额外开销,为了避免误判,所以采用哨兵集群,多个哨兵一起监控,基于少数服从多数原则,主观下线到客观下线;
哨兵之间是如何互相通信:
基于 Redis 提供的发布 / 订阅机制(pub/sub 机制) ,在主库上有一个名为“__sentinel__:hello”的频道,不同哨兵通过它来发布/订阅消息,实现互相通信;而且只有订阅了同一个频道的应用,才能通过发布的消息进行信息交换。
- 哨兵 1连接相关信息(IP端口)发布到主库“__sentinel__:hello”频道上,哨兵 2 和 3 订阅该频道。
- 哨兵 2 和 3 就可以从这个频道直接获取哨兵 1连接信息,以这样的方式哨兵集群就形成了,实现各个哨兵互相通信。
哨兵之间是如何选主:
新主库选举按照 一定条件筛选出的符合条件的从库,并按照 一定规则对其进行打分,最高分者为新主库。基于某一轮评出最高分从库就选举结束,哨兵发起主从切换。
一定条件包括:
- 从库的当前在线状态
- 判断它之前的网络连接状态,通过down-after-milliseconds * num(断开连接次数),当断开连接次数超过阈值,不适合为新主库。
一定规则包括:
- 从库优先级 , 通过slave-priority 配置项,给不同的从库设置不同优先级,优先级最高的从库得分高;
- 从库复制进度,和旧主库同步程度最接近的从库得分高,通过repl_backlog_buffer缓冲区记录主库 master_repl_offset 和从库slave_repl_offset 相差最小高分;
- 从库 ID 号 , ID 号小的从库得分高。
选举完新的主库后,不能每个哨兵都发起主从切换,需要选举成leader哨兵,才能执行主从切换;也基于少数服从多数原则"投票仲裁"选举出来,当任何一个哨兵判定主库“主观下线”后,发送 s-master-down-by-addr命令想要成为Leader的信号,其他哨兵根据与主机连接情况作出相对的响应,赞成票Y,反对票N,而且如果有多个哨兵发起请求,每个哨兵的赞成票只能投给其中一个,其他只能为反对票。
成为Leader哨兵满足两个条件:
- 获得半数以上的赞成票;
- 获得的票数同时还需要大于等于哨兵配置文件中的quorum值。
Leader哨兵发送消息主从库切换,端户端就会接收到新主库的连接信息:
switch-master <master name> <oldip> <oldport> <newip> <newport>
三、Redis集群
集群(Redis Cluster),是Redis 3.0开始引入的分布式存储方案。
- 由多个节点(Node)组成,Redis的数据分布在这些节点中,突破了Redis单机内存大小的限制,存储容量大大增加;
- 集群中的节点分为主节点和从节点:只有主节点负责读写请求!!和集群信息的维护;从节点负责高可用,只进行主节点数据和状态信息的复制。
- 集群支持主从复制和主节点的自动故障转移(故障转移的选举有所有主节点进行,与哨兵类似);当任一节点发生故障时,集群仍然可以对外提供服务。
四、数据问题
1、主备切换后, 异步复制导致的少量数据丢失
主从数据在异步复制过程中,master有部分数据还没复制到slave就宕机了,则未同步这部分数据就丢失了
2、主从复制的过程,异步复制导致数据不一致
在主从异步复制过程中,当从库因为网络延迟或执行复杂度高命令阻塞导致滞后执行同步命令,这样就会导致数据不一致;
解决方法:
可以开发一个外部程序来监控主从库间的复制进度(master_repl_offset 和 slave_repl_offset ),通过监控 master_repl_offset 与slave_repl_offset差值得知复制进度,当复制进度不符合预期设置的Client不再从该从库读取数据。