目录配置主从架构Redis 搭建主从架构的三种方式断开主从结构主从架构的拓扑结构一主一从拓扑​一主多从拓扑树形拓扑主从复制数据同步同步的载体和进度表示实时复制全量复制部分复制更多replid2psync传输延迟主从复制总流程查看主从结构信息Redis支持主从架构。使用主从架构可以提高服务并发量也可以提高服务可用性。但是主从架构涉及到一个关键的问题就是主从复制即主节点和从节点之间的数据同步问题本篇文章对此展开讲解关于提高服务并发量方面主要是提高了读操作并发量因为主节点可以写的节点只有一个全部的写操作仍旧由一个节点承担。这也是没有办法的事因为如果有多个主节点的话数据同步会更加麻烦不过可以配合其他方案来解决这个问题。关于提高可用性方面在主从架构中一个主节点负责写多个从节点负责读。一但从节点挂了对整个服务几乎没有影响一但主节点挂了服务仍旧可读只是暂时不能写不至于全都挂了。配置主从架构Redis 搭建主从架构的三种方式以下三种方式都可以将某一个redis服务变成另一个redis服务的从节点masterHost和masterPort分别表示主节点服务的IP和端口号配置文件方式在 Redis 配置文件如redis.conf中添加以下指令重启 Redis 后永久生效slaveof {masterHost} {masterPort}启动命令参数方式在启动 Redis 服务时通过命令行参数指定主节点信息仅对本次启动生效redis-server --slaveof {masterHost} {masterPort}运行时动态指令方式连接至 Redis 客户端后执行以下命令即时生效无需重启重启后失效slaveof {masterHost} {masterPort}配置完成后从节点不再能够修改数据可以在配置文件中修改slave-read-only来解除限制但是一般不这样做。断开主从结构slaveof no one执行上述命令的客户端所连接的redis服务不再是从节点。如果想要永久生效那就把配置文件的相关配置内容删除后重启服务即可。主从架构的拓扑结构一主一从拓扑在这种结构下主节点可能因为写需求过于频繁而压力过大此时可以关闭主节点的AOF模式来缓解压力。但是这样一来如果主节点崩溃不能立即重启主节点重启后内存中的数据可能会丢失很多从节点一但同步主节点会把很多数据也删除掉。因此如果主节点崩溃应该让主节点使用从节点的AOF文件进行重启而不是直接重启。当然具体情况还得根据业务来看如果说redis只是作为提高性能的缓存并且对速度不敏感也就直接重启了。一主多从拓扑这种结构也存在主节点并发压力大的问题同时还存在主节点带宽压力大的问题因为主节点要给多个从节点同步数据树形拓扑这种结构也存在主节点并发压力大的问题但是缓解了主节点带宽压力大的问题因为把同步任务分派给了各级非叶子节点而不只是主节点。不过也引入了同步延迟大导致数据不一致更加明显的问题因为是逐级同步的。主从复制数据同步主从复制大致有三种复制方式它们不是三种排他的选择而是共同构成了redis主从复制系统同步的载体和进度表示主节点和从节点要进行数据同步必然需要主节点给从节点传输一些什么这里就有两种选择二进制数据主节点执行过的命令如果选择二进制数据的话一但主节点不是添加键值对而是修改原有键值对主节点就需要告知从节点哪个位置被修改了这又涉及到了位置问题修改成了什么否则每次数据同步主节点都需要把所有数据都发给从节点来保证数据一致。而选择命令的话主节点只需要不断把自己执行过的命令让从节点也执行一遍即可其他的都不用考虑。因此把命令作为同步的载体是最高效最自然的选择。节点累积执行的命令长度字节数以下称为offset就可以表示该节点的同步进度或者总的进度如果是主节点。主节点会将最近执行过的命令以及到该命令为止的offset一一对应保存到backlog缓冲区内主要是为全量复制和部分复制的触发提供了判断依据间接保证了主从同步的可靠性以及为部分复制提供了需要复制的部分命令下面读者会有所体会。为什么backlog不仅保存命令还要保存到该命令为止的offsetbacklog缓冲区是有限的一但命令积压过多之前的命令就会被覆盖但是这些命令可能并没有被从节点同步这就造成了主从节点数据不一致却不自知的问题。有了“到该命令为止的offset”之后从节点可以用自己的offset和缓冲区命令对应的offset对比如果判断发现自己的offset相对于缓冲区中最小offset差了不止一个命令长度的话就说明丢失命令了此时就要触发全量复制。同时可以通过偏移量知道哪些命令已经复制哪些没有复制支持部分复制。实时复制实时复制是由主节点向从节点发起的、目标是实时同步少量数据的报文主从节点成功建立复制连接后主节点会将自身接收到的所有数据修改操作通过 TCP 长连接持续同步给从节点并不断增加双方offset表示进度从节点执行同步过来的命令修改本地数据以此保证主从之间的数据一致性。该复制长连接依靠应用层自研心跳包维持链路存活并非 TCP 协议自带的保活心跳完整心跳检测规则如下主、从节点双向实现心跳检测逻辑双方都会以客户端身份向对方发起通信主节点默认每 10 秒向从节点发送ping命令用于探测从节点是否存活、复制链路是否正常从节点默认每 1 秒向主节点发送replconf ack {offset}指令上报自身当前已同步完成的复制偏移量offset超时断开规则 若主节点检测到与从节点的通信延迟超过配置项repl-timeout默认 60 秒会判定从节点已离线主动断开该复制客户端连接待从节点重新建立复制连接后心跳检测流程自动恢复。全量复制全量复制是由从节点向主节点发起的、目标是一次性同步主节点所有数据的请求从节点向主节点发起全量复制的请求主节点解析请求发送一个FULLRESYNC响应其中包含主节点自身的运行时信息这个用于部分复制之后会解释从节点把主节点发来的运行时信息保存下来主节点使用bgsave创建新的子进程来把调用bgsave那一刻主节点的内存数据以及主节点那一刻的offset保存成RDB文件在此过程中主节点还可以接受客户端发来的命令并执行这些命令会被缓存一份在backlog缓冲区里主节点offset持续增加。主节点向从节点发送RDB文件主节点向从节点发送backlog缓冲区里的最新命令它们修改的内容是前面的RDB文件所没有包含的从节点读取RDB文件中的数据到内存里并使用RDB文件中保存的offset更新自己的offset从节点读取命令同步数据并不断增加自己的offset如果从节点开启了AOF在全量复制过后从节点会主动进行AOF的重写缩小AOF的体积因为可能一下子新增了大量数据Redis 从 2.8.18 版本开始支持无磁盘复制。主节点在执行 RDB 生成流程时不会生成 RDB 文件到磁盘中了而是直接把生成的 RDB 数据通过网络发送给从节点。这样就节省了一系列的写硬盘和读硬盘的操作开销。不过即使这样全量复制仍旧是一种很耗费资源无论是网络带宽还是主从节点的CPU、内存等资源的复制方式。为什么使用RDB文件不是说命令才是同步载体吗在全量复制这种大数据量的网络传输RDB这种压缩二进制文件很节省资源况且一旦涉及到全量复制很可能是从节点缺很多这种情况下主节点的backlog也存不下那么多的命令。部分复制部分复制发生在特定的情况下是一种避免全量复制的优化手段当主从节点之间因为网络抖动而断开TCP连接从节点重连主节点成功后不必进行全量复制而是可以接着上次连接继续同步数据这样就节省了很多资源提高了效率。现在的问题就是从节点怎么知道重连后的主节点还是原来的主节点如果不是原来的主节点还继续同步而不全量复制主从节点的数据就会不一致毕竟我们可以把任何一个节点变为主节点。解决这个问题的方法就是replicationidreplicationid(replid)是一个40位长度的随机字符串。每个主节点都会维护一个replid无论它本来就是主节点还是从节点变成主节点。维护replid的目的是唯一地标识一个历史数据集。当主从节点第一次建立连接的时候主节点把自己的replid发送给从节点从节点将其保存下来等到从节点重连成功后它会首先发送这个replid给新主节点如果新主节点的replid和这个相同说明还是原来的主节点历史数据集如果不是说明不是原来的主节点这个时候就要进行全量复制了。除此之外在断开连接的整个过程中主节点有可能又执行了许多命令如果这些命令把之前从节点没有同步的、存放在backlog缓冲区的命令覆盖了这时候从节点只能全量复制主节点而不能触发部分复制。所以该如何进一步判断是否能触发部分复制呢解决这个问题的方法就是offset从节点重连成功后不仅仅发送replid给主节点还发送从节点此时的offset如果从节点的offset小于backlog缓冲区中最小的命令对应的offset说明已经有几个命令被新命令覆盖了需要进行全量复制反之可以继续部分复制综上部分复制的整个流程如下当主从节点之间出现网络中断时如果超过 repl-timeout 时间主节点会认为从节点故障并终端复制连接。主从连接中断期间主节点依然响应命令但这些复制命令都因网络中断无法及时发送给从节点所以暂时将这些命令滞留在backlog中。当主从节点网络恢复后从节点再次连上主节点。从节点将之前保存的 replicationId 和 复制偏移量发送给主节点请求进行部分复制全量复制的时候要主节点发送给从节点的运行时信息。主节点进行必要的验证replicationid与主节点的replicationid是否相同如果相同进行下一步否则响应FULLRESYNC并退出流程offset是否小于backlog缓冲区内命令对应的最小offset如果否响应CONTINUE这是进行部分复制的标志否则响应FULLRESYNC并退出流程主节点将需要从节点同步的数据发送给从节点最终完成一致性。更多replid2其实主节点除了维护replid之外还维护一个replid2。简单来说一个从节点晋升为主节点之后会自己生成一个replid同时把上一个主节点的replid存在replid2字段中。这样有一个好处即使新的主节点是由从节点晋升而来其他从节点和其同步的时候也有一定概率触发部分复制如果offset符合要求因为他们原来同属一个复制拓扑数据很相近。这进一步提升了效率。当然在判断的流程中要把判断条件从“replid 主节点replid”变成 “replid 主节点replid || replid 主节点replid2”这就引出了另一个知识点其实从节点也可能会维护backlog缓冲区为自己晋升为主节点做准备有了backlog就有更大可能触发部分复制而不是全量复制以此提升效率psync上述全量复制和部分复制其实都是服务器通过psync命令来发起的psync replication offset如果想全量复制输入psync -1如果想尝试部分复制输入psync replid offsetreplid是从节点保存的数据集标识符offset是从节点当前的同步进度传输延迟主从节点一般部署在不同机器上复制时的网络延迟就成为需要考虑的问题Redis 为我们提供了repl-disable-tcp-nodelay参数用于控制是否关闭 TCP_NODELAY默认为 no即开启 TCP_NODELAY 功能说明如下当为no时主节点产生的命令数据无论大小都会及时地发送给从节点这样主从之间延迟会变小但增加了网络带宽的消耗。适用于主从之间的网络环境良好的场景如同机房部署。当为yes时主节点会合并较小的 TCP 数据包从而节省带宽。默认发送时间间隔取决于 Linux 的内核一般默认为 40 毫秒。这种配置节省了带宽但增大主从之间的延迟。适用于主从网络环境复杂的场景如跨机房部署。主从复制总流程第一次建立连接从节点向主节点发送psync -1一次性同步所有数据。之后主节点不断向从节点发起实时复制当遇到突发状况的时候从节点会先发送psync replid offset 尝试部分复制以提高效率如果不行则进行全量复制之后又一次进入实时复制 ... ....查看主从结构信息info replication