Redis的主从复制

目录

  • [1. 文章前言](#1. 文章前言)
  • [2. 配置Redis服务器](#2. 配置Redis服务器)
    • [2.1 建立主从复制](#2.1 建立主从复制)
    • [2.2 断开主从复制](#2.2 断开主从复制)
    • [2.3 安全性、只读、传输延迟](#2.3 安全性、只读、传输延迟)
  • [3. 主从复制的拓扑结构](#3. 主从复制的拓扑结构)
    • [3.1 一主一从结构](#3.1 一主一从结构)
    • [3.2 一主多从结构](#3.2 一主多从结构)
    • [3.3 树形主从结构](#3.3 树形主从结构)
  • [4. 实现原理](#4. 实现原理)
    • [4.1 主从复制过程](#4.1 主从复制过程)
    • [4.2 数据同步psync命令](#4.2 数据同步psync命令)
    • [4.3 全量复制](#4.3 全量复制)
    • [4.4 部分复制](#4.4 部分复制)
    • [4.5 复制积压缓冲区](#4.5 复制积压缓冲区)
    • [4.6 实时复制](#4.6 实时复制)
  • [5. 主从复制总结](#5. 主从复制总结)

1. 文章前言

(1)在分布式系统中为了解决单点问题,通常会把数据复制多个副本部署到其他服务器,满足故障恢复和负载均衡等需求。Redis 也是如此,它为我们提供了复制的功能,实现了相同数据的多个Redis副本。复制功能是高可用Redis的基础,哨兵和集群都是在复制的基础上构建的。

(2)本章内容如下:

  • 介绍复制的使用方式:如何建立或断开复制、安全性、只读等。
  • 说明复制可支持的拓扑结构,以及每个拓扑结构的适用场景。
  • 分析复制的原理,包括:建立复制、全量复制、部分复制、心跳检测等。

2. 配置Redis服务器

2.1 建立主从复制

(1)参与复制的Redis实例划分为主节点(master) 和从节点(slave) 。每个从结点只能有一个主节点,而一个主节点可以同时具有多个从结点。复制的数据流是单向的,只能由主节点到从节点。配置复制的方式有以下三种:

  1. 在配置文件中加入slaveof {masterHost} {masterPort}随Redis启动生效。
  2. 在redis-server启动命令时加入--slaveof {masterHost} {masterPort}生效。
  3. 直接使用redis命令:slaveof {masterHost} {masterPort}生效。
    接下来,我们将redis.conf配置文件复制一份redis-slave.conf,并且修改其daemonize为yes。
bash 复制代码
# By default Redis does not run as a daemon. Use 'yes' if you need it.
# Note that Redis will write a pid file in /var/run/redis.pid when daemonized.
daemonize yes

(2)接下来,默认启动的redis作为主Redis,重新通过命令行启动一个Redis实例作为从Redis:

bash 复制代码
# ubuntu
redis-server /etc/redis/redis-slave.conf --port 6380 --slaveof 127.0.0.1 6379
# centos
redis-server /etc/redis-slave.conf --port 6380 --slaveof 127.0.0.1 6379

注意:修改配置主要是修改从机的配置,主机配置不变。

(3)通过netstat -nlpt确保两个Redis均已正确启动。

bash 复制代码
[root@host ~]# netstat -nlpt
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State
tcp 0 0 127.0.0.1:6379 0.0.0.0:* LISTEN
tcp 0 0 127.0.0.1:6380 0.0.0.0:* LISTEN

(4)通过redis-cli可以连接主Redis实例,通过redis-cli -p 6380连接从Redis。并且观察复制关系。

bash 复制代码
127.0.0.1:6379> set hello world
OK
127.0.0.1:6379> get hello
"world"
127.0.0.1:6380> get hello
"world"

从运行结果中看到复制已经工作了,针对主节点6379的任何修改都可以同步到从节点6380中,复制过程如图所示。

(5)可以通过info replication命令查看复制相关状态。

  • 主节点6379复制状态信息:
bash 复制代码
127.0.0.1:6379> info replication
# Replication
role:master
connected_slaves:1
slave0:ip=127.0.0.1,port=6380,state=online,offset=100,lag=0
master_replid:2fbd35a8b8401b22eb92ff49ad5e42250b3e7a06
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:100
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:100 
  • 从节点6380复制状态信息:
bash 复制代码
127.0.0.1:6380> info replication
# Replication
role:slave
master_host:127.0.0.1
master_port:6379
master_link_status:up
master_last_io_seconds_ago:1
master_sync_in_progress:0
slave_repl_offset:170
slave_priority:100
slave_read_only:1
connected_slaves:0
master_replid:2fbd35a8b8401b22eb92ff49ad5e42250b3e7a06
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:170
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:170

2.2 断开主从复制

(1)slaveof命令不但可以建立复制,还可以在从节点执行slaveof no one来断开与主节点复制关系。例如在6380节点上执行slaveof no one来断开复制。断开复制主要流程:

  1. 断开与主节点复制关系。
  2. 从节点晋升为主节点。

(2)从节点断开复制后并不会抛弃原有数据,只是无法再获取主节点上的数据变化。通过slaveof命令还可以实现切主操作,将当前从节点的数据源切换到另一个主节点。执行slaveof {newMasterlp} {newMasterPort}命令即可。切主操作主要流程:

  1. 断开与旧主节点复制关系。
  2. 与新主节点建立复制关系。
  3. 删除从节点当前所有数据。
  4. 从新主节点进行复制操作。

2.3 安全性、只读、传输延迟

(1)安全性:

  • 对于数据比较重要的节点,主节点会通过设置requirepass参数进行密码验证,这时所有的客户端访问必须使用auth命令实行校验。
  • 从节点与主节点的复制连接是通过-一个特殊标识的客户端来完成,因此需要配置从节点的masterauth参数与主节点密码保持一致, 这样从节点才可以正确地连接到主节点并发起复制流程。

(2)只读:

  • 默认情况下,从节点使用slave-read-only=yes配置为只读模式。由于复制只能从主节点到从节点,对于从节点的任何修改主节点都无法感知,修改从节点会造成主从数据不一致。所以建议线上不要修改从节点的只读模式。

(3)传输延迟:主从节点一般部署在不同机器上,复制时的网络延迟就成为需要考虑的问题,Redis 为我们提供了repl-disable-tcp-nodelay 参数用于控制是否关闭TCP_ NODELAY,默认为no,即开启tcp-nodelay功能,说明如下:

  • 当关闭时,主节点产生的命令数据无论大小都会及时地发送给从节点,这样主从之间延迟会变小,但增加了网络带宽的消耗。适用于主从之间的网络环境良好的场景,如同机房部署。
  • 当开启时,主节点会合并较小的TCP数据包从而节省带宽。默认发送时间间隔取决于Linux的内核,一般默认为40毫秒。这种配置节省了带宽但增大主从之间的延迟。适用于主从网络环境复杂的场景,如跨机房部署。

3. 主从复制的拓扑结构

Redis的复制拓扑结构可以支持单层或多层复制关系,根据拓扑复杂性可以分为以下三种:一主一从、一主多从、树状主从结构。

3.1 一主一从结构

(1)一主一从结构是最简单的复制拓扑结构,用于主节点出现宕机时从节点提供故障转移支持,如图所示。当应用写命令并发量较高且需要持久化时,可以只在从节点上开启AOF,这样既可以保证数据安全性同时也避免了持久化对主节点的性能干扰。但需要注意的是,当主节点关闭持久化功能时,如果主节点宕机要避免自动重启操作。一主一从拓扑图:

3.2 一主多从结构

(1)一主多从结构(星形结构)使得应用端可以利用多个从节点实现读写分离,如图所示。对于读比重较大的场景,可以把读命令负载均衡到不同的从节点上来分担压力。同时一些耗时的读命令可以指定一台专门的从节点执行,避免破坏整体的稳定性 。对于写并发量较高的场景,多个从节点会导致主节点写命令的多次发送从而加重主节点的负载。一主多从拓扑图:

3.3 树形主从结构

(1)树形主从结构(分层结构)使得从节点不但可以复制主节点数据,同时可以作为其他从节点的主节点继续向下层复制 。通过引入复制中间层,可以有效降低住系欸按负载和需要传送给从节点的数量,如图所示。数据写入节点A之后会同步给B和C节点,B节点进一步把数据同步给D和E节点。当主节点需要挂载等多个从节点时为了避免对主节点的性能干扰,可以采用这种拓扑结构。树形拓扑图:

4. 实现原理

4.1 主从复制过程

(1)如图所示,下面详细介绍建立复制的完整流程。从图中可以看出复制过程大致分为6个过程:

  1. 保存主节点(master) 的信息。开始配置主从同步关系之后,从节点只保存主节点的地址信息,此时建立复制流程还没有开始,在从节点6380执行info replication可以看到如下信息:
bash 复制代码
master_host: 127.0.0.1
master_port: 6379
master_link_status: down

从统计信息可以看出,主节点的ip和port被保存下来,但是主节点的连接状态

(master_link_status) 是下线状态。

  1. 从节点(slave) 内部通过每秒运行的定时任务维护复制相关逻辑,当定时任务发现存在新的主节点后,会尝试与主节点建立基于TCP的网络连接。如果从节点无法建立连接,定时任务会无限重试直到连接成功或者用户停止主从复制。
  2. 发送ping命令。连接建立成功之后,从节点通过ping命令确认主节点在应用层上是工作良好的。如果ping命令的结果pong回复超时,从节点会断开TCP连接,等待定时任务下次重新建立连接。
  3. 权限验证。如果主节点设置了requirepass参数,则需要密码验证,从节点通过配置masterauth参数来设置密码。如果验证失败,则从节点的复制将会停止。
  4. 同步数据集。对于首次建立复制的场景,,主节点会把当前持有的所有数据全部发送给从节点 ,这步操作基本是耗时最长的,所以又划分称两种情况:全量同步和部分同步。
  5. 命令持续复制。当从节点复制了主节点的所有数据之后,针对之后的修改命令,主节点会持续的把命令发送给从节点,从节点执行修改命令,保证主从数据的一致性。

4.2 数据同步psync命令

(1)Redis使用psync命令完成主从数据同步,同步过程分为:全量复制和部分复制。

  • 全量复制:一般用于初次复制场景,Redis 早期支持的复制功能只有全量复制,它会把主节点全部数据一次性发送给从节点,当数据量较大时,会对主从节点和网络造成很大的开销。
  • 部分复制:用于处理在主从复制中因网络闪断等原因造成的数据丢失场景,当从节点再次连上主节点后,如果条件允许,主节点会补发数据给从节点。因为补发的数据远小于全量数据,可以有效避免全量复制的过高开销。

(2)PSYNC命令的语法格式:

bash 复制代码
PSYNC replicationid offset 
  • 如果replicationid设为什么??并且offset设为-1此时就是在尝试进行全量复制.
  • 如果replicationid offset 设为了具体的数值,则是尝试进行部分复制.

(3)replicationid/replid (复制id):

  • 主节点的复制id。主节点重新启动,或者从节点晋级成主节点,都会生成一个replicationid. (同一个节点,每次重启,生成的replicationid也会变化)。
  • 从节点在和主节点建立连接之后,就会获取到主节点的replicationid。
  • 通过info replication 即可看到replicationid。
bash 复制代码
127.0.0.1:6379> info replication
# Replication
role:master
connected_slaves:0
master_replid:1da596acecf5a34b4b2aae45bd35be785691ae69
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:0
second_repl_offset:-1
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0

关于master_replid 和master_replid2。每个节点需要记录两组master_replid。这个设定解决的问题场景是这样的:

  • 比如当前有两个节点A和B,A为master,B为slave。
  • 此时B就会记录A的master_replid。
  • 如果网络出现抖动,B以为A挂了,B自己就会成为主节点。于是B给自己分配了新的master_ replid。
  • 此时就会使用master_replid2来保存之前A的master_replid.
  • 后续如果网络恢复了,B就可以根据master_ replid2 找回之前的主节点。
  • 后续如果网络没有恢复,B就按照新的master_ replid自成一派,继续处理后续的数据。

(4)offset (偏移量):

  • 参与复制的主从节点都会维护自身复制偏移量。主节点(master) 在处理完写入命令后,会把命令的字节长度做累加记录,统计信息在info replication中的master_repl_offset 指标中。
bash 复制代码
127.0.0.1:6379> info replication
# Replication
role:master
...
master_repl_offset:1055130
  • 从节点(slave) 每秒钟上报自身的复制偏移量给主节点,因此主节点也会保存从节点的复制偏移量,统计指标如下:
bash 复制代码
127.0.0.1:6379> info replication
connected_slaves:1
slave0:ip=127.0.0.1,port=6380,state=online,offset=1055214,lag=1
...
  • 从节点在接受到主节点发送的命令后,也会累加记录自身的偏移量。统计信息在info replication 中的。slave_repl_offset 指标中:
bash 复制代码
127.0.0.1:6380> info replication
# Replication
role:slave
...
slave_repl_offset:1055214
  • 复制偏移量的维护如图所示。通过对比主从节点的复制偏移量,可以判断主从节点数据是否一致。

(5)replid + offset共同标识了一个"数据集" 。如果两个节点,他们的replid和offset 都相同,则这两个节点上持有的数据,就一定相同。psync的云行流程:

  • 从节点发送psync命令给主节点,replid 和offset的默认值分别是?和-1。
  • 主节点根据psync参数和自身数据情况决定响应结果:
    • 如果回复+FULLRESYNC replid offset, 则从节点需要进行全量复制流程。
    • 如果回复+CONTINEU,从节点进行部分复制流程。
    • 如果回复-ERR,说明Redis主节点版本过低,不支持psync命令。从节点可以使用sync命令进行
  • 全量复制:
    • psync一般不需要手动执行。Redis会在主从复制模式下自动调用执行。
    • sync会阻塞redis server处理其他请求。psync则不会。

4.3 全量复制

(1)全量复制是Redis最早支持的复制方式,也是主从第-次建立复制时必须经历的阶段。全量复制的运行流程如图所示。

  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文件。

(2)通过分析全量复制的所有流程,我们会发现全量复制是一件高成本的操作。主节点bgsave的时间,RDB在网络传输的时间,从节点清空旧数据的时间,从节点加载RDB的时间等。所以一般应该尽可能避免对已经有大量数据集的Redis进行全量复制。

(3)有磁盘复制Vs无磁盘复制(diskless):

  • 默认情况下,进行全量复制需要主节点生成RDB文件到主节点的磁盘中,再把磁盘上的RDB文件通过发送给从节点。
  • Redis从2.8.18版本开始支持无磁盘复制 。主节点在执行RDB生成流程时,不会生成RDB文件到磁盘中了,而是直接把生成的RDB数据通过网络发送给从节点.这样就节省了一系列的写硬盘和读硬盘的操作开销。

4.4 部分复制

(1)部分复制主要是Redis针对全量复制的过高开销做出的一种优化措施,使用psync replicationld offset命令实现 。当从节点正在复制主节点时,如果出现网络闪断或者命令丢失等异常情况时,从节点会向主节点要求补发丢失的命令数据,如果主节点的复制积压缓冲区存在数据则直接发送给从节点,这样就可以保持主从节点复制的一致性。补发的这部分数据一般远远小于全量数据 ,所以开销很小。整体流程如图所示。

  1. 当主从节点之间出现网络中断时,如果超过repl-timeout 时间,主节点会认为从节点故障并终端复制连接。
  2. 主从连接中断期间主节点依然响应命令,但这些复制命令都因网络中断无法及时发送给从节点,所以暂时将这些命令滞留在复制积压缓冲区中。
  3. 当主从节点网络恢复后,从节点再次连上主节点。
  4. 从节点将之前保存的replicationld和复制偏移量作为psync的参数发送给主节点,请求进行部分复制。
  5. 主节点接到psync请求后,进行必要的验证。随后根据offset去复制积压缓冲区查找合适的数据,并响应+CONTINUE给从节点。
  6. 主节点将需要从节点同步的数据发送给从节点,最终完成一致性。

4.5 复制积压缓冲区

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

(2)由于缓冲区本质上是先进先出的定长队列,所以能实现保存最近已复制数据的功能,用于部分复制和复制命令丢失的数据补救。复制缓冲区相关统计信息可以通过主节点的info replication中:

bash 复制代码
127.0.0.1:6379> info replication
# Replication
role:master
...
repl_backlog_active:1 // 开启复制缓冲区
repl_backlog_size:1048576 // 缓冲区最⼤⻓度
repl_backlog_first_byte_offset:7479 // 起始偏移量,计算当前缓冲区可⽤范围
repl_backlog_histlen:1048576 // 已保存数据的有效⻓度

根据统计指标,可算出复制积压缓冲区内的可用偏移量范围: [repl backlog_first_byte_offset, repl_backlog_first_byte_offset + repl backlog_histlen]。这个相当于一个基于数组实现的环形队列上述区间中的值就是"数组下标"。

如果当前从节点需要的数据,已经超出了主节点的积压缓冲区的范围,则无法进行部分复制,只能全量复制了。

4.6 实时复制

(1)主从节点在建立复制连接后,主节点会把自己收到的修改操作,通过tcp长连接的方式,源源不断的传输给从节点。从节点就会根据这些请求来同时修改自身的数据。从而保持和主节点数据的一致性。

(2)另外,这样的长连接,需要通过心跳包的方式来维护连接状态。(这里的心跳是指应用层自己实现的心跳,而不是TCP自带的心跳)。

  1. 主从节点彼此都有心跳检测机制,各自模拟成对方的客户端进行通信。
  2. 主节点默认每隔10秒对从节点发送ping命令,判断从节点的存活性和连接状态。
  3. 从节点默认每隔1秒向主节点发送replconf ack {offset}命令,给主节点上报自身当前的复制偏移量。

如果主节点发现从节点通信延迟超过repl-timeout配置的值(默认 60秒),则判定从节点下线,断开复制客户端连接。从节点恢复连接后,心跳机制继续进行。

5. 主从复制总结

(1)主从复制解决的问题就是单点问题:

  1. 单个 redis 节点,可用性不高。
  2. 单个 redis 节点,性能有限。

(2)主从复制的特点:

  1. Redis 通过复制功能实现主节点的多个副本。
  2. 主节点用来写,从节点用来读。这样做可以降低主节点的访问压力。
  3. 复制支持多种拓扑结构,可以在适当的场景选择合适的拓扑结构。
  4. 复制分为全量复制,部分复制和实时复制。
  5. 主从节点之间通过心跳机制保证主从节点通信正常和数据⼀致性。

(3)主从复制配置的过程:

  1. 主节点配置不需要改动。
  2. 从节点在配置文件中加入 slaveof 主节点ip 主节点端口的形式即可。

(4)主从复制的缺点:

  1. 从机多了,复制数据的延时非常明显。
  2. 主机挂了,从机不会升级成主机。只能通过人工干预的方式恢复。
相关推荐
HEU_firejef15 分钟前
redis——布隆过滤器
数据库·redis·缓存
一只小爪子16 分钟前
Redis 常用配置项说明
linux·运维·数据库·redis
散修-小胖子1 小时前
InnoDB 事务系统(一):认识事务
数据库·mysql
闲人陈二狗1 小时前
MySQL的详细使用教程
数据库·mysql
怒码ing1 小时前
etcd+京东hotkey探测使用
数据库·etcd
Source、1 小时前
ETCD备份还原
数据库·etcd
张声录11 小时前
【ETCD】【实操篇(二)】如何从源码编译并在window上搭建etcd集群?
数据库·etcd
张声录11 小时前
【ETCD】ETCD 的一致性读(Linearizable Read)流程解析
服务器·数据库·etcd
梧桐树04291 小时前
python:正则表达式
数据库·python·正则表达式
Snow_Dragon_L2 小时前
【MySQL】表操作
linux·数据库·后端·sql·mysql·ubuntu