redis的主从复制

79次阅读

共计 5996 个字符,预计需要花费 15 分钟才能阅读完成。

前言

本来以前在 csdn 写文章,只是无意间注册了 sf 账号,每天逛一下这些网站,发现 sf 质量确实很高,而且,也没有那么多让人糟心的广告,每次在 csdn 看到想读的文章都第一件事拉到最下面看是不是广告,如果不是才耐心的从头开始读,这种体验很糟心,就来到 sf,而且发现sf 就在杭州本地,不错不错,果断支持一下。

主从复制是哨兵 sentinelcluster的基础,知其然知其所以然,所以,了解了其中的细节原理和每一步都执行了什么操作,对以后的调试也是很有帮助的。

为什么要使用主从

在实际的场景当中单一节点的 redis 容易面临风险,比如只有一台 redis,然后挂了,本来要经过 缓存层 的东西直接把压力转到 数据库 上了,即使数据库扛得住,响应速度可能也不是很快,这样业务受影响了,如果有主从复制,一台从机挂了还可以在其他从机获取数据,所以,主从复制可以很好的解决 单机器故障 的问题,也就是 高可用 问题,但是其实还存在 故障转移问题,后面我会写文章解决这个。

主从复制的简单流程

1. 这里我把一系列操作举例放到实际业务流程中,比如有 N 多用户在频繁的读取数据。
2. 如果用户 1 先去 redis-slave 缓存里取数据。
3. 缓存中没有数据,去数据库里取。
4.database proxy分发读取数据操作,请求落在 mysql 从节点上。
5.database proxy将获取到的数据(这里假如获取到了,没有获取到直接返回给 app)写入 redis-master 主节点。
6. 同时把获取到的数据返回给用户。
7. 后面的N 个用户获取相同的数据,压力被分散在不同的 redis-slave 上面,即使,某一个 redis-slave 宕机了,其他 redis-slave 还可以继续提供服务。
注:
主从复制数据的复制是 单向的 ,只能由主节点到从节点默认情况下,每台redis 服务器都是主节点,也就是 主节点挂了其他从节点可以变成主节点 (这个切换过程我附在下面);且一个主节点可以有多个从节点(或没有从节点),但 一个从节点只能有一个主节点

主从复制搭建

从节点开启主从复制,有 3 种方式:

1、配置文件启用

在从服务器的配置文件中加入:slaveof masterip masterport

2、启动命令启用

redis-server 启动命令后加入 --slaveof masterip masterport

3、客户端命令启用

redis 服务器启动后,直接通过客户端执行命令:slaveof masterip masterport,则该 redis 实例成为从节点。
通过 info replication 命令可以看到复制的一些信息
注:Log 文件位置 vi /var/log/redis/redis.log

主从复制原理

主从复制过程大体可以分为 3 个阶段:连接建立阶段(即准备阶段)、数据同步阶段、命令传播阶段。
在从节点执行 slaveof 命令后,复制过程便开始运作,复制过程大致分为 6 个过程。

对照日志看一下日志如下

1保存主节点(master)信息。
2从节点(slave)内部通过每秒运行的定时任务维护复制相关逻辑,当定时任务发现存在新的主节点后,会尝试与该节点建立网络连接从节点会建立一个 socket 套接字,从节点建立了一个端口为 xxxxx 的套接字,专门用于接受主节点发送的复制命令。从节点连接成功后打印如下日志:

如果从节点无法建立连接,定时任务会无限重试直到连接成功或者执行 slaveof no one 取消复制
关于连接失败,可以在从节点执行 info replication 查看 master_link_down_since_seconds 指标,它会记录与主节点连接失败的系统时间。从节点连接主节点失败时也会每秒打印如下日志,方便发现问题:

 Error condition on socket for SYNC: {socket_error_reason}

3发送 ping 命令
连接建立成功后从节点发送 ping 请求进行首次通信,ping 请求主要目的如下:
检测主从之间网络套接字是否可用。
检测主节点当前是否可接受处理命令。
如果发送 ping 命令后,从节点没有收到主节点的 pong 回复或者超时,比如网络超时或者主节点正在阻塞无法响应命令,从节点会断开复制连接,下次定时任务会发起重连。
我估计好多人的学软件的第一课 都 ping pong 过把,这里就不解释了。。

4权限验证。如果主节点设置了 requirepass 参数,则需要密码验证,从节点必须配置 masterauth 参数保证与主节点相同的密码才能通过验证;如果验证失败复制将终止,从节点重新发起复制流程。
ping 服务器和端口,通了肯定校验一下密码

5同步数据集。主从复制连接正常通信后,对于首次建立复制的场景,主节点会把持有的数据全部发送给从节点,这部分操作也是耗时最长的步骤。
6命令持续复制。当主节点把当前的数据同步给从节点后,便完成了复制的建立流程。接下来主节点会持续地把写命令发送给从节点,保证主从数据一致性。

全量复制和部分复制和复制偏移量

全量复制

用于初次复制或其它无法进行部分复制的情况,将主节点中的所有数据都发送给从节点,是一个非常重型的操作,当数据量较大时,会对主从节点和网络造成很大的开销

1.Redis 内部会发出一个同步命令,刚开始是 Psync 命令,Psync ? - 1 表示要求 master 主机同步数据
2. 主机会向从机发送 runidoffset,因为 slave 并没有对应的 offset,所以是 全量复制
3. 从机 slave 会保存 主机 master 的基本信息 save masterInfo
4.主节点收到全量复制的命令后,执行 bgsave(异步执行),在后台生成RDB 文件(快照),并使用一个缓冲区(称为 复制缓冲区 )记录从现在开始执行的所有写命令
5. 主机 send RDB 发送 RDB 文件给从机
6. 发送缓冲区数据
7. 刷新旧的数据,从节点在载入主节点的数据之前要先将老数据清除
8. 加载 RDB 文件将数据库状态更新至主节点执行 bgsave 时的数据库状态和缓冲区数据的加载

全量复制的开销主要如下

1.bgsave 时间
2.RDB 文件网络传输时间
3. 从节点清空数据的时间
4. 从节点加载 RDB 的时间

部分复制

用于处理在主从复制中因网络闪断等原因造成的数据丢失场景,当从节点再次连上主节点后,如果条件允许,主节点会补发丢失数据给从节点。因为补发的数据远远小于全量数据,可以有效避免全量复制的过高开销,需要注意的是,如果网络中断时间过长,造成主节点没有能够完整地保存中断期间执行的写命令,则无法进行部分复制,仍使用全量复制
部分复制是 redis 2.8 以后出现的,之所以要加入部分复制,是因为全量复制会产生很多问题,比如像上面的时间开销大、无法隔离等问题,redis 希望能够在 master 出现抖动(相当于断开连接)的时候,可以有一些机制将复制的损失降低到最低。

1.如果网络抖动(连接断开 connection lost
2.主机 master 还是会写 replbackbuffer(复制缓冲区)
3. 从机 slave 会继续尝试连接主机
4. 从机 slave 会把自己当前 runid 和偏移量传输给主机 master,并且执行 pysnc 命令同步
5. 如果 master 发现你的偏移量是在缓冲区的范围内,就会返回 continue 命令
6. 同步了 offset 的部分数据,所以部分复制的基础就是偏移量 offset

复制偏移量

参与复制的主从节点都会维护自身复制偏移量。主节点(master)在处理完写入命令后,会把命令的字节长度做累加记录,统计信息在 info relication 中的 master_repl_offset 指标中:

从节点(slave)每秒钟上报自身的复制偏移量给主节点,因此主节点也会保存从节点的复制偏移量,统计指标如下:

从节点在接收到主节点发送的命令后,也会累加记录自身的偏移量。统计信息在 info relication 中的 slave_repl_offset

复制积压缓冲区

复制积压缓冲区是保存在主节点上的一个固定长度的队列,默认大小为1MB,当主节点有连接的从节点(slave)时被创建,这时主节点(master)响应写命令时,不但会把命令发送给从节点,还会写入复制积压缓冲区。

命令传播阶段 ,主节点除了将写命令发送给从节点,还会发送一份给复制积压缓冲区,作为写命令的备份;除了存储写命令,复制积压缓冲区中还存储了其中的每个字节对应的复制偏移量(offset)。由于复制积压缓冲区定长且先进先出,所以它保存的是主节点最近执行的写命令;时间较早的写命令会被挤出缓冲区。可以对照全量复制redis-master 的图理解 复制挤压缓冲区。

正常情况下 redis 是如何决定是全量复制还是部分复制

1. 从节点将 offset 发送给主节点后,主节点根据 offset 和缓冲区大小决定能否执行部分复制:
2. 如果 offset 偏移量之后的数据,仍然都在复制积压缓冲区里,则执行部分复制;
3. 如果 offset 偏移量之后的数据已不在复制积压缓冲区中(数据已被挤出),则执行全量复制。

1、缓冲区大小调节:

由于缓冲区长度固定且有限,因此可以备份的写命令也有限,当主从节点 offset 的差距过大超过缓冲区长度时,将无法执行部分复制,只能执行全量复制。反过来说,为了提高网络中断时部分复制执行的概率,可以根据需要增大复制积压缓冲区的大小 (通过配置repl-backlog-size) 来设置;例如如果网络中断的平均时间是 60s,而主节点平均每秒产生的写命令(特定协议格式) 所占的字节数为100KB,则复制积压缓冲区的平均需求为6MB,保险起见,可以设置为12MB,来保证绝大多数断线情况都可以使用部分复制。

2、服务器运行 ID(runid)

每个 redis 节点 (无论主从),在启动时都会自动生成一个随机 ID(每次启动都不一样),由 40 个随机的十六进制字符组成;runid 用来唯一识别一个redis 节点。通过 info server 命令,可以查看节点的runid


主从节点初次复制时,主节点将自己的 runid 发送给从节点,从节点将这个 runid 保存起来;当断线重连时,从节点会将这个 runid 发送给主节点;主节点根据 runid 判断能否进行部分复制:
如果从节点保存的 runid 与主节点现在的 runid 相同,说明主从节点之前同步过,主节点会继续尝试使用部分复制 (到底能不能部分复制还要看offset 和复制积压缓冲区的情况)
如果从节点保存的 runid 与主节点现在的 runid 不同,说明从节点在断线前同步的 Redis 节点并不是当前的主节点,只能进行全量复制。

主从复制进阶常见问题解决

1、读写分离

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


虽然读写有优势,能够让读这部分分配给各个 slave 从机,如果不够,直接加 slave 机器就好了。但是也会出现以下问题。
1. 复制数据延迟。
可能会出现 slave 延迟导致读写不一致等问题,当然你也可以使用监控偏移量 offset,如果 offset 超出范围就切换到 master 上, 逻辑切换,而具体延迟多少,可以通过 info replicationoffset 指标进行排查。
对于无法容忍大量延迟场景,可以编写外部监控程序监听主从节点的复制偏移量,当延迟较大时触发报警或者通知客户端避免读取延迟过高的从节点
同时从节点的 slave-serve-stale-data 参数也与此有关,它控制这种情况下从节点的表现:如果为 yes(默认值),则从节点仍能够响应客户端的命令;如果为no,则从节点只能响应infoslaveof 等少数命令。该参数的设置与应用对数据一致性的要求有关;如果对数据一致性要求很高,则应设置为 no
2. 从节点故障问题
对于从节点的故障问题,需要在客户端维护一个可用从节点可用列表,当从节点故障时,立刻切换到其他从节点或主节点。

2、主从配置不一致

主机和从机不同,经常导致主机和从机的配置不同,并带来问题。
数据丢失:主机和从机有时候会发生配置不一致的情况,例如 maxmemory 不一致,如果主机配置 maxmemory8G,从机 slave 设置为4G,这个时候是可以用的,而且还不会报错。但是如果要做高可用,让从节点变成主节点的时候,就会发现数据已经丢失了,而且无法挽回。虽然错误很低级,但是有人会犯。。。

3、规避全量复制

全量复制指的是当 slave 从机断掉并重启后,runid 产生变化而导致需要在 master 主机里拷贝全部数据。这种拷贝全部数据的过程非常耗资源。全量复制是不可避免的,例如第一次的全量复制是不可避免的,这时我们需要选择小主节点,且 maxmemory 值不要过大,这样就会比较快。同时选择在低峰值的时候做全量复制。

1. 造成全量复制的原因
1、是主从机的运行 runid 不匹配。

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

2、复制缓冲区空间不足

比如默认值 1M,可以部分复制。但如果缓存区不够大的话,首先需要网络中断,部分复制就无法满足。其次需要增大复制缓冲区配置(relbacklogsize),对网络的缓冲增强。参考之前的说明。
解决办法:
在一些场景下,可能希望对主节点进行重启,例如主节点内存碎片率过高,或者希望调整一些只能在启动时调整的参数。如果使用普通的手段重启主节点,会使得 runid 发生变化,可能导致不必要的全量复制。
为了解决这个问题,rdis 提供了 debug reload 的重启方式:重启后,主节点的 runidoffset都不受影响,避免了全量复制。

3、master 主机挂了重启

当一个主机下面挂了很多个 slave 从机的时候,主机 master 挂了,这时 master 主机重启后,因为 runid 发生了变化,所有的 slave 从机都要做一次全量复制。这将引起单节点和单机器的复制风暴,开销会非常大。
解决办法:
可以采用树状结构降低多个从节点对主节点的消耗
从节点采用树状树非常有用,网络开销交给位于中间层的从节点,而不必消耗顶层的主节点。但是这种树状结构也带来了运维的复杂性,增加了手动和自动处理故障转移的难度

4、单机器的复制风暴

由于 redis的单线程架构,通常单台机器会部署多个 redis 实例。当一台机器(machine)上同时部署多个主节点(master)时,如果每个 master 主机只有一台 slave 从机,那么当机器宕机以后,会产生大量全量复制。这种情况是非常危险的情况,带宽马上会被占用,会导致不可用。
解决办法:
应该把主节点尽量分散在多台机器上,避免在单台机器上部署过多的主节点。
当主节点所在机器故障后提供故障转移机制,避免机器恢复后进行密集的全量复制。

正文完
 0

Redis的主从复制

79次阅读

共计 1285 个字符,预计需要花费 4 分钟才能阅读完成。

Redis 的主从复制
1. 主从复制
Redis 配置成主从模式,主库(Master)只负责写数据,从库(Slave)只负责读数据。
注意:一个主库可以拥有多个从库,但一个从库只能隶属于一个主库。
将选为 Slave 的机器上配置:
# REPLICATION
slave of <master ip> <port>
masterauth <password>
开启主从关系后,数据会自动进行复制。
2. 全量复制和部分复制
Redis 的复制方式包括全量复制和部分复制。
2.1 全量复制
流程如下:

Slave 发出一个同步命令,要求 Master 同步数据;
Master 向 Salve 发送 runid 和 offset;当 Slave 上没有 offset 记录时,执行全量复制;
Slave 执行 save masterinfo,保存 Master 的基本信息;
Master 执行 bgsave 生成快照;
Master 执行 send RDB 将快照发送到 Slave 的缓冲区;
Slave 更新旧的 RDB 文件,并加载新的 RDB;

由上述流程可以看到,全量复制的开销主要在:

生成 RDB 文件,即 bgsave;
RDB 服务器间传输;
如果有 AOF 设置,达到重写阈值,会进行 AOF 重写;

2.2 部分复制
部分复制在 Redis 2.8 之后开始支持,可以减少全量复制的开销。
部分复制的原理:

每台机器启动后都会有一个与当前进程相关的 runid;
每个机器在写入数据后会有一个偏移量 master_repl_offset;
通过偏移量来判断 Slave 与 Master 直接的数据差多少;

部分复制常用在主从连接断开、Master 抖动时。
流程如下:

主从连接断开;
Slave 尝试连接主机;Master 写入复制缓冲区 repl_back_buffer;
恢复连接后,Slave 将自己当前 runid 和偏移量传输给 Master,并请求同步数据;
Master 检查偏移量是否在缓冲区范围内,如果是,则进行部分复制;如果不是,则进行全量复制;

优点:部分复制直接使用缓冲区的数据进行 RDB 同步,相比全量复制,减少了 RDB 的生成和传输开销;同时,也减少了 AOF 重写阈值达到的几率。
3. 主从复制中的问题
3.1 读写分离的问题

复制数据延迟:Slave 延迟导致读写不一致。
监控偏移量 offset,如果超出范围就将读节点切换到 Master 上,并重新全量复制 Slave 节点。

读到过期数据:Redis 采用懒惰性策略和采样式策略。

懒惰性策略指 Redis 只有当操作 key 时才去看数据是否过期;采样式策略指定期会去采样,如果是过期的,就自动删除。当过期数量非常多的时候,采样速度比不上逻辑数据变化的速度,Slave 没有写权限,只有 Master 可以删除,就会出现过期数据。
Redis 3.2 以上版本修复此问题。

Slave 节点故障
Slave 节点通过持久化数据与主节点进行部分复制同步;Redis2.8 实现 Slave 恢复后部分复制同步;

Master 节点故障

需要手动切换主从关系;
使用 Redis 哨兵模式自动完成主从切换;

3.2 复制风暴
Master 重启,多个 Slave 会需要复制。这个时候需要更换复制拓扑,通过在 Slave 下再分从机,减少主机 Master 的压力。

正文完
 0