分布式系统中的几种Redis部署方式

为了解决一个程序只部署在一个服务器上的单点问题:

  1. 可用性问题,如果这个机器挂了,就意味着服务就中断了

  2. 一个程序只部署在一台机器上,它的性能/支持的并发量也是有限的

所以,就引入了分布式系统。在分布式系统中,往往希望有多个服务器来部署Redis服务,从而构成一个Redis集群,这样就可以让这个集群给整个分布式系统中的其他服务,提供更稳定/更高效的数据存储功能。

主从模式

在一个集群中,有的是主节点,有的是从节点。从节点的数据要跟随主节点变化,从节点的数据要和主节点保持一致。

本来,在主节点上保存的所有数据,在引入从节点之后,就要把主节点上的数据复制出来,放在从节点中,后续,主节点对于数据有任何的修改,都会把这样的修改同步到从节点上。

从节点就是主节点的副本。

Redis主从模式中,从节点上的数据,不允许修改,只能读取!

对于客户端发来的读取数据的操作,可以在主从节点中的任意一个节点中获取数据;对于写操作,则需要从主节点操作,或者从节点告知主节点进行操作,这样也就提高了性能和并发量。

对于之前的单个Redis服务器节点,如果机器挂了,整个Redis就挂了。但现在可以有多个节点,也可以把这些节点放在不同的机房中,这样就不会出现所有节点同时挂掉的情况。如果挂掉的是某一个从节点,不影响读写操作,如果挂掉的是主节点,则只能进行读操作。一定程度上提高了可用性。

更准确的说,主从模式,主要是针对读操作进行并发量和可用性的提高;对于写操作,无论是可用性还是并发,都非常依赖主节点,而且主节点只能有一个。好在实际业务场景中,读操作往往比写操作更频繁。

主节点上会收到源源不断的修改数据的请求,从节点需要从主节点这里同步这些修改请求。但是,从节点和主节点之间的数据同步,不是瞬间完成的。

每一个从节点中都会有一个offset字段,用来记录同步数据的进度

主从复制中的拓扑结构(若干节点之间,按照什么样的方式进行组织连接):

一主一从

Redis中的主从复制插图

一个主节点连接一个从节点,主节点和从节点都可以处理读数据请求,只有主节点可以处理写数据请求。

如果写数据请求太多,也会给主节点造成压力,这时可以关闭主节点的AOF,只在从节点上开启AOF,让主节点只操作内存中的数据,减少IO操作。

缺点在于,这种模式下,主节点一旦挂了,就不能让他自动重启,因为没有AOF文件,如果自动重启,就会丢失数据,进一步的主从同步,会把从节点的数据也删除掉。

改进办法:主节点挂了之后,让其从从节点获取到AOF文件,再启动。

一主多从

因为实际的开发中,读请求往往远远超过写请求,所以应该适量增加从节点的数量以提高效率

Redis中的主从复制插图(1)

主节点上的数据发生改变,会把改变的数据同时同步给所有从节点。

但是,随着从节点的数量增多,主节点要同步一条数据就需要传输多次,所以往往需要增加主节点的网络带宽,增大了成本。

树形结构

Redis中的主从复制插图(2)

这种结构,不要求主节点有太高的网络带宽,从节点也可以帮助数据同步,但是进行数据修改时,同步的延时更长了。

主从复制流程

Redis中的主从复制插图(3)

数据同步的方法

Redis提供了psync命令,可以完成数据同步的过程。

这个命令不需要手动执行,Redis服务器会在建立好主从同步关系之后,自动执行psync(从节点执行psync,从节点从主节点拉取数据)。

进行数据复制,需要从节点在与主节点建立好复制关系之后,从主节点获取到replication id(复制ID)。这个ID是主节点启动时自动生成的,每个主节点每次重启得到的ID都是不同的。根据这个ID就可以知道当前从节点是从哪个主节点复制的数据(在集群中会有多个主节点)。

每一个节点中都还会有一个offset值,对于主节点,每次有新的修改数据的操作时,都会在这个值原来的基础上增加;对于从节点,每同步一部分主节点中的数据,这个值也会增加,当从节点中的offset值跟主节点中的offset值相等时,就证明数据同步完成。

replication id 和 offset 共同描述了一个“数据集合”

如果有两个机器,replication id 一样,offset 也一样,就可以认为这两个 redis 机器上存储的数据是完全一样的。

psync运行流程:

Redis中的主从复制插图(4)

  1. 从节点发送 psync 命令给主节点,replid 和 offset 的默认值分别是?和-1.

  2. 主节点根据 psync 参数和自身数据情况决定响应结果:

  • 如果回复 +FULLRESYNC replid offset,则从节点需要进行全量复制流程

  • 如果回复 +CONTINUE,从节点进行部分复制流程

  • 如果回复 -ERR,说明Redis主节点版本过低,不支持 psync 命令,从节点可以使用 sync 命令进行全量复制

psync 可以从主节点获取全量数据,也可以获取一部分数据,具体区别于 offset 的进度。

如果 offset 为-1,则获取全量数据;如果是具体的整数,则从当前偏移量位置进行获取部分数据。

复制全量数据会有比较大的消耗,所以一般还是部分数据复制更好,但是如果主节点本身并不适合部分复制,就会仍然给从节点进行全量复制。

较老的版本的Redis不支持psync,可以用sync代替,不过后者会阻塞其他操作,所以不建议使用。

一般什么时候会进行部分复制?

从节点之前已经从主节点上复制过数据了,但因为网络抖动导致从节点重启了,这时需要重新行主节点同步数据,在大部分数据已经同步的情况下,希望只同步一小部分未复制的数据。

全量复制流程

Redis中的主从复制插图(5)

  1. 从节点发送 psync 命令给主节点进行数据同步,由于是第一次进行复制,从节点没有主节点的运行 ID 和复制偏移量,所以发送 psync ? -1。

  2. 主节点根据命令,解析出要进行全量复制,回复 +FULLRESYNC 响应。

  3. 从节点接收主节点的运行信息进行保存。

  4. 主节点执行 bgsave 进行 RDB 文件的持久化。

  5. 主节点发送 RDB 文件给从节点,从节点保存 RDB 数据到本地硬盘。

  6. 主节点将从生成 RDB 到接收完成期间执行的写命令,写入缓冲区中,等从节点保存完 RDB 文件后,主节点再将缓冲区内的数据补发给从节点,补发的数据仍然按照 RDB 的二进制格式追加写入到收到的 RDB 文件中,保持主从一致性。

  7. 从节点清空自身原有数据。

  8. 从节点加载 RDB 文件得到与主节点一致的数据。

  9. 如果从节点加载 RDB 完成之后,并且开启了 AOF 持久化功能,它会进行 bgrewrite 操作,得到最近的 AOF 文件。

第四步中,使用 RDB 是因为二进制节省空间,不能使用已有的 RDB 文件,而要重新生成,是因为已有的文件和当前的最新数据可能存在较大差异。

上述由主节点向从节点传送数据使用的是 RDB 文件,要写入文件就要有IO操作,效率就会降低。所以,为了保证效率,Redis 主节点进行全量复制的时候,也支持“无硬盘模式”(diskless),也就是主节点生成的 RDB 的二进制数据,不再直接保存到文件中,而是直接进行网络传输。同样的,从节点也不需要先把接收到的数据写入硬盘,然后再加载,而是直接把收到的数据写入内存。

但是,即使引入了无硬盘模式,整个操作仍然是比较重量级的,比较耗时的,网络传输是无法省去的,所以效率也并没有提升很多。

关于 replication id 和 runid

具有主从关系的节点之间的 replication id 是相同的,但是每个节点的 runid 都是不同的。

runid 主要用于支撑实现 Redis 哨兵的功能,和主从复制没什么关系。而 replication id 在上文中也表明,它主要是用于主从复制。

部分复制流程

Redis中的主从复制插图(6)

  1. 当主从节点之间出现网络中断时,如果超过 repl-timeout 时间,主节点会认为从节点故障并中断复制连接。

  2. 主从连接中断期间主节点依然响应命令,但这些复制命令都因网络中断无法及时发送给从节点,所以暂时将这些命令滞留在复制积压缓冲区(一个基于数组实现的环形队列)中。

  3. 当主从节点网络恢复后,从节点再次连接上主节点。

  4. 从节点将之前保存的 replication 和 offset 作为 psync 的参数发送给主节点,请求进行部分复制。

  5. 主节点接到 psync 请求后,进行必要的验证,随后根据 offset 去复制积压缓冲区查找合适的数据,并响应 +CONTINUE 给从节点。

  6. 主节点将需要从节点同步的数据发送给从节点,最终完成一致性。

上文中提到的“主节点有时候会判定适不适合进行部分复制,不适合的话就进行全量复制”,其实指的是,从节点虽然之前进行过数据复制,但是后续需要复制的数据已经超出积压缓冲区中的数据了,这时只把积压缓冲区中的数据发送给从节点是不够的,所以只能进行全量复制。

实时复制

  • 全量复制:从节点刚连上主节点之后,进行的数据初始化工作。

  • 部分复制:全量复制的特殊情况,优化手段,目的和全量复制一样。

  • 实时复制:从节点已经和主节点同步好了数据,但是之后主节点会源源不断地收到新的修改数据的请求,主节点上的数据就会随之改变,这些数据也同样需要同步给从节点。

实时复制需要主从节点之间建立 TCP 长连接,随后主节点把自己收到的修改数据的请求发送给从节点,从节点再根据这些修改请求修改内存中的数据。

在进行实时复制时,需要保证连接处于可用状态,所采用的是“心跳包”机制。

  • 主节点:默认每隔10秒给从节点发送一个 ping 命令,从节点收到就返回 pong

  • 从节点:默认每隔1秒给主节点发起一个特定的请求,上报当前从节点复制数据的进度(offset)

本站无任何商业行为
个人在线分享 » Redis中的主从复制
E-->