Redis第6课:Redis 复制

要实现分布式数据库的更大的存储容量和高并发访问量,我们会将原来集中式数据库的数据分别存储到其他多个网络节点上。Redis 为了解决这个单一节点的问题,也会把数据复制多个副本部署到其他节点上进行复制,实现 Redis的高可用,实现对数据的冗余备份,从而保证数据和服务的高可用

应该说,Redis 复制是高可用的基石。 没有 Redis 复制,也就不可能实现高可用。然而,Redis 复制这块经常出现开发和运维故障,在我们排错之前,需要对 Redis 有一个清晰的认识。

下面我们将从以下五个方面全面认识 Redis 复制。

  • 什么是主从复制
  • 复制的配置
  • 全量复制和部分复制
  • 故障如何处理
  • 开发和运维遇到的常见问题

在此之前,我们需要明确将 Redis 应用到工程项目中时,只用一台 Redis 显然是万万不能的,为什么不能呢?主要概括为以下三个要点。

1.机器故障。我们部署到一台 Redis 服务器,当发生机器故障时,需要迁移到另外一台服务器并且要保证数据是同步的。而数据是最重要的,如果你不在乎,基本上也就不会使用 Redis 了。

2.容量瓶颈。当我们有需求需要扩容 Redis 内存时,从 16G 的内存升到 64G,单机肯定是满足不了。当然,你可以重新买个 128G 的新机器。但是我们非要这么做吗?我将会在后续讲到。

3.QPS瓶颈。Redis 号称支持10万 QPS,当业务需要100万 QPS 时,我们该怎么做呢?这时就用到了 Redis 复制。本文讲解的主要内容便是 Redis 复制(Replication)。

什么是主从复制


Redis第6课:Redis 复制


如上图所示,我们将 Redis 服务器作为 Master 主库,另外一台作为 Slave 从库,主库只是负责写数据,每次有数据更新的时候,Redis 会将数据同步到其他的从库中,而从库只是负责读数据。

当然你还可以根据业务需求,增加更多的从库,如下图所示,红色的 Redis 为主库,蓝色的是三台从库。


Redis第6课:Redis 复制


很多人都觉得这没什么好说的,但在这里我还是想说这么做的两大好处。

1.实现了读写分离,读写分离不仅可以提高服务器的负载能力,同时可根据需求的变化,改变从库的数量,第一张图中只有一个从库,你还可以像第二张图那样增加至两个、三个……你觉得这个优点怎么样;

2.数据备份了多份,如果一台机器宕机,你可以从其他机器上快速恢复。但需要注意的是一台主库可以拥有多个从库,但一个从库却只能属于一个主库。

复制的配置

接下来,我们讲一下主从复制的作用。首先请看下面这张图。


Redis第6课:Redis 复制


如该图所示,Master 属于主节点,Slave 属于从节点。我们在主机上执行 set 和 incr 命令,在从库中通过 get 得到这些数据,这正好说明 Redis 复制是自动完成的。当 Master 节点宕机以后,Slave节点可以支援主节点。

除了一主一从,Redis 还支持一主多从,这样我们就可以获得更多高可用的可能性。比如:当一个主机和一个从机都宕机了,那么还有几台从机可以做备份;再比如,当 Master 节点的流量超过最大值的时候,可以通过从机对流量进行分流。

总而言之,主从复制的作用主要是提供数据副本及扩展 Redis 读的性能。

接下来,我们再来配置一下主从架构,这是为一些刚开始学习的朋友准备的,通过实际的配置,以便于大家更好的理解。

1.安装两台服务器的实例,关于如何安装 Redis,我在第一篇文章已经讲到。我将主库端口设置为6379,从库设置为6380。bind 都设置为127.0.0.1。


Redis第6课:Redis 复制


2.在 Slave 实例中我们增加了 slaveof 127.0.0.1 6379 的配置。从库配置相同。如下图所示。


Redis第6课:Redis 复制


配置后,需要启动这两个实例,如输出下面内容,说明主从复制的架构已经配置成功。


Redis第6课:Redis 复制


这里唯一需要说明的是,主库和从库的端口号不能相同,否则不可能同时启动。

全量复制和部分复制

在讲解全量复制和部分复制之前,我们先来讲一下,runid 和偏移量的概念

什么是runid,每次 Redis 启动的时候,Redis 就会有一个运行的 ID,这个 ID 只在 Redis 运行的时候才有,如果关闭 runid 就不存在了。runid 的作用是一个标识,如果主库去复制从库的数据,就需要根据这个 runid 去复制。


Redis第6课:Redis 复制


上图所示,通过 redis-cli -p 6379 info server | grep run 显示出6379端口的 runid 和6380端口的 runid

很多 Redis 第一次启动的时候,压根就不知道其他机器上面的数据,这个时候我们就需要全量复制。

什么是偏移量呢?偏移量就是记录到底写了多少数据,比如我在6379端口的 Redis 中,set k1 v1这个命令就是写入一个字节。这个时候它同步给6380端口,这个时候6380也会记录偏移量。

主 Redis 每次向从 Redis 传播 N个字节的数据时,都会在自己的复制偏移量上加 N; 同理,从 Redis 每次接收到 N 个字节时,也会在自己的复制偏移量上加 N。


Redis第6课:Redis 复制


上图所示,我们首先运行 redis-cli -p 6379 info replication,就能在里面找到 masterreploffset:1865,然后做了一次 set 操作,再运行 redis-cli -p 6379 info replication,此时就能看到偏移值变为了1950,如果你去其他从库去运行,也会变成1950,偏移量是记录部分复制的依据。大家只要了解即可,生产环境中不怎么关心这个值。

全量复制

我们来看下 Redis 全量复制的流程图。


Redis第6课:Redis 复制


如图所示,第一步 Redis 内部会发出一个同步命令,刚开始是 Psync 命令,Psync ? -1表示要求 master 主机同步数据;第二步 master 会向从机发送 runid 和 offset,因为 slave 并没有对应的 offset,所以是全量复制;第三步,从机 slave 会保存 master 的基本信息 save masterInfo;第四步,master 会执行 bgsave 命令(持久化命令),作为一个快照来说,就是怎么快怎么来;实际上 master 主机里面会有 replbackbuffer(复制缓冲区);第五步,send RDB 发送 RDB 文件;第六步,发送缓冲区数据;第七步,刷新旧的数据;第八步,加载 RDB 文件和缓冲区数据的加载。

这就是建立全量数据副本的功能。希望大家结合图能够了解 Redis 如何做全量复制的流程,在大脑里有一个印象。

那么全量复制需要哪些开销呢?主要有以下几项。

  • bgsave 时间
  • RDB 文件网络传输时间
  • 从节点清空数据的时间
  • 从节点加载 RDB 的时间
  • AOF 重写的时间(这里需要说明一下,RDB 全量复制完加载 RDB,如果 AOF 开启的话,就会出现 AOF 重写来保证是最新的)。

部分复制

部分复制是 Redis 2.8 以后出现的,之所以要加入部分复制,是因为全量复制会产生很多问题,比如像上面的时间开销大、无法隔离等问题, Redis 希望能够在 master 出现抖动(相当于断开连接)的时候,可以有一些机制将复制的损失降低到最低。


Redis第6课:Redis 复制


如图所示,第一步如果打算抖动(连接断开 connection lost);第二步 master 还是会写 replbackbuffer(复制缓冲区);第三步,slave 会继续尝试连接主机;第四步,slave 会把自己当前 runid 和偏移量传输给主机 master,并且执行 pysnc 命令同步;第五步,如果 master 发现你的偏移量是在缓冲区的范围内,就会返回 continue 命令;第七步,同步了 offset 的部分数据,所以部分复制的基础就是偏移量 offset。

通过部分复制,可以有效的减少全量复制的开销。

故障如何处理

讲解完主从复制和部分复制,我们来讲一下 Redis 复制的故障如何处理。在开发运维的时候,故障是不可避免的。如果你是单机的 Redis ,通常如果通过短信或者电话告知你错误的时候,你是一脸懵逼的,通常这种故障发生在半夜。

但是如果我们使用了自动故障转移,那效果就不同了。即使半夜出现故障,你还是可以好好休息,等早上起来再去完成故障排除。

主从结构的故障转移

主要考虑主机 master 宕机、从机 slave 宕机两种情况。


Redis第6课:Redis 复制


上图所示,一主二从的结构,如果某一台从机宕机了。这个时候我们需要把客户端传到宕机的请求改成第一个 slave 从机就可以了。基本不会有什么问题。

但如果是 master 宕机,master 就会和两个 slave 从机断掉,这个时候该怎么办呢?如下图所示。


Redis第6课:Redis 复制


当 master 宕机,原来从 master 读的客户端会通过 slaveof no one 读写 slave 从机,发生该命令的 slave 从机会变成 master 主机,而另一台从机会执行 slaveof new master 命令,会让另外一台 slave 知道是哪台变成 master 并且与它发生同步。

上述两种情况其实并不是自动的,那我们如何让这两种情况变成自动化呢,相关的 Redis Sentinel 知识会在下一篇讲解。Redis Sentinel可以真正实现高可用,它会自动设置好哪台是 master,当 master 宕机,就会自动把某一台 slave 从机变成 master,并且告诉所有 slave 从机哪台变成了 master 并自动连接。

开发和运维中的问题

我将从下面四点来说明。

  • 读写分离
  • 主从配置不一致
  • 规避全量复制
  • 规避复制风暴

读写分离

读流量分摊到从节点。这是个非常好的特性,如果一个业务只需要读数据,那么我们只需要连一台 slave 从机读数据。


Redis第6课:Redis 复制


虽然读写有优势,能够让读这部分分配给各个 slave 从机,如果不够,直接加 slave 机器就好了。但是也会出现以下问题。

1.复制数据延迟。可能会出现 slave 延迟导致读写不一致等问题,当然你也可以使用监控偏移量 offset,如果 offset 超出范围就切换到 master 上。 2.读到过期数据。Redis 采用懒惰性策略和采样式策略,懒惰性策略指的是 Redis 操作 key,它才去看 key 有没有过期数据。采样式策略是指定期会去采样,如果是过期的,就自动删除。当过期数量非常多的时候,我的采样速度比不上逻辑数据变化的速度,slave 没有删除权限,只有 master 有,这个时候就会出现过期数据。但是如果你用 Redis 3.2 以上,就没有这个问题了。 3.从节点故障。怎么对发生故障的从节点进行迁移。

配置不一致

主机和从机不同,经常导致主机和从机的配置不同,并带来下列两种问题。

1.数据丢失:主机和从机有时候会发生配置不一致的情况,例如 maxmemory 不一致,如果主机配置 maxmemory 为8G,从机 slave 设置为4G,这个时候是可以用的,而且还不会报错。但是如果要做高可用,让从节点变成主节点的时候,就会发现数据已经丢失了,而且无法挽回。

2.数据结构优化参数导致不一致:hash-max-ziplist-enties 参数,如果主机对这些优化参数设置了,从机 slave 却没有优化,就会发生数据不一致的情况。

规避全量复制

全量复制指的是当 slave 从机断掉并重启后,runid 产生变化而导致需要在 master 主机里拷贝全部数据。这种拷贝全部数据的过程非常耗资源。

全量复制是不可避免的,例如第一次的全量复制是不可避免的,这时我们需要选择小主节点,且maxmemory 值不要过大,这样就会比较快。同时选择在低峰值的时候做全量复制

造成全量复制的原因之一是主从机的运行 runid 不匹配。解释一下,主节点如果重启,runid 将会发生变化。如果从节点监控到 runid 不是同一个,它就会认为你的节点不安全。当发生故障转移的时候,如果主节点发生故障,那么从机就会变成主节点。我们会在后面讲解哨兵和集群。

造成全量复制的第二个原因是复制缓冲区空间不足,比如默认值1M,可以部分复制。但如果缓存区不够大的话,首先需要网络中断,部分复制就无法满足。其次需要增大复制缓冲区配置(relbacklogsize),对网络的缓冲增强。默认是1M,我们实际会设置成10M左右。

规避复制风暴

当一个主机下面挂了很多个 slave 从机的时候,主机 master 挂了,这时 master 主机重启后,因为 runid 发生了变化,所有的 slave 从机都要做一次全量复制。这将引起单节点和单机器的复制风暴,开销会非常大。


Redis第6课:Redis 复制


1.单节点复制风暴。当主节点重启,多从节点会复制。这个时候需要更换复制拓扑。下图就是改变拓扑结构的问题,通过在 slave 下再分从机,可以有效的减少主机 master 的压力。


Redis第6课:Redis 复制


2.单机器的复制风暴。如上图,如果每个 master 主机只有一台 slave 从机,那么当机器宕机以后,会产生大量全量复制。这种情况是非常危险的情况,带宽马上会被占用,会导致不可用。这个问题在实际运维中必须注意。建议这种情况将单机器改成 Redis Sentinel。这样可以自动将从机变成主机 master。

如果你有疑问欢迎加微信咨询:


https://u.wechat.com/MDHWBbNbJRv36gtFwfExkEo (二维码自动识别)


也可以关注我的公众号想我提问:


http://weixin.qq.com/r/EEzl-dLE5Y_arZae9xkC (二维码自动识别)