前言

默认情况下,每台Redis服务器都是主节点;
由于个人服务器性能原因,以下的所有操作都是单机集群的概念!在实际工作中并不会这样配置,而是使用哨兵模式来监控!这篇文章的意义主要就是为了让大家了解主从复制这个概念!

概念

主从复制,是指将一台Redis服务器的数据,复制到其他的Redis服务器。前者称为主节点(master/leader),后者称为从节点(slave/follower);数据的复制是单向的,只能由主节点到从节点。Master以写为主,Slave 以读为主。

主要作用:
①数据冗余:主从复制实现了数据的热备份,是持久化之外的一种数据冗余方式。

②故障恢复:当主节点出现问题时,可以由从节点提供服务,实现快速的故障恢复;实际上是一种服务的冗余。

③负载均衡:在主从复制的基础上,配合读写分离,可以由主节点提供写服务,由从节点提供读服务(即写Redis数据时应用连接主节点,读Redis数据时应用连接从节点),分担服务器负载;尤其是在写少读多的场景下,通过多个从节点分担读负载,可以大大提高Redis服务器的并发量。

④高可用(集群)基石:除了上述作用以外,主从复制还是哨兵和集群能够实施的基础,因此说主从复制是Redis高可用的基础。

环境配置(单机集群)

  1. 基本查看命令:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
127.0.0.1:6379> ping  #测试是否连接成功!
PONG
127.0.0.1:6379> info replication #查看当前redis信息
# Replication
role:master #角色--主机
connected_slaves:0 #从机数量为0
master_replid:b9565cf2edea63b7e9860f3ef1a170d59ff7a4d4 #唯一标识的id
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
  1. 开启三台服务:

①复制三个配置文件:

20210126143632916

②修改以下配置:
端口:

20210126143722422

pid名:

20210126143837459

log文件名:

2021012614390448

dump.rdb名:

2021012614393199

repl-disable-tcp-nodelay:

image-20250506172656413

​ 该属性用于设置是否禁用 TCP 特性 tcp-nodelay。设置为 yes 则禁用 tcp-nodelay,此时master 与 slave 间的通信会产生延迟,但使用的 TCP 包数量会较少,占用的网络带宽会较小。相反,如果设置为 no,则网络延迟会变小,但使用的 TCP 包数量会较多,相应占用的网络带宽会大。

​ tcp-nodelay:为了充分复用网络带宽,TCP 总是希望发送尽可能大的数据块。为了达到该目的,TCP 中使用了一个名为 Nagle 的算法。

​ Nagle 算法的工作原理是,网络在接收到要发送的数据后,并不直接发送,而是等待着数据量足够大(由 TCP 网络特性决定)时再一次性发送出去。这样,网络上传输的有效数据比例就得到了大大提升,无效数据传递量极大减少,于是就节省了网络带宽,缓解了网络压力。

​ tcp-nodelay 则是 TCP 协议中 Nagle 算法的开头。

③全部启动并查看:

image-20230131180732247

查看所有Redis端口:证明启动成功啦!

20210126144549129

一主二从(单机测试)

  1. 认大哥大!!!
image-20230131180806822
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
127.0.0.1:6380> ping
PONG
127.0.0.1:6380> slaveof 127.0.0.1 6379 #让本机认6379的机器为大哥!
OK
127.0.0.1:6380> info replication #查看信息
# Replication
role:slave #从机
master_host:127.0.0.1 #主机ip
master_port:6379 #主机端口
master_link_status:up
master_last_io_seconds_ago:3
master_sync_in_progress:0
slave_repl_offset:14
slave_priority:100
slave_read_only:1
connected_slaves:0
  1. 第二台机器同理,我们看看主机的信息:
image-20230131180846079
1
2
3
4
5
6
7
8
9
10
127.0.0.1:6379> ping
PONG
127.0.0.1:6379> info replication
# Replication
role:master #主机
connected_slaves:2 #有两台从机
#从机的ip、端口等信息
slave0:ip=127.0.0.1,port=6380,state=online,offset=56,lag=1
#从机的ip、端口等信息
slave1:ip=127.0.0.1,port=6381,state=online,offset=56,lag=1

出现以上内容,证明我们配置成功了!

  1. 注意点:这种通过命令的配置是’一次性的’,如果机器宕机、断电等,就需要重新认大哥大!

在实际工作中,我们都是通过配置文件中修改指定配置的!如下图:

image-20230131180912646

可以修改以上配置来实现主从机的配置!

  1. 测试读写操作:

①主机写,从机读
写:

20210126150841396

读:

2021012615091430

证明主从复制成功了!

②如果主机断开

20210126151029554

从机可以正常读数据:

20210126151045629

查看从机信息:

image-20230131181505891 image-20230131181551412

证明,虽然主机断开了,但是从机还是可以正常读取原先就有的数据的!

③如果断开的主机重新连接上

image-20230131181610165

从机也可正常连接上主机,因为配置了,会自动寻找主机。

④如果从机断开重连呢?

20210126151429797

image-20230131181634117 image-20230131181646644

证明:如果从机断开重连,不会自动连接上主机!因为我们的配置是在从机上写的,而且是命令写的,重启时会重置!
⑤从机能写嘛?

20210126151640666

从机只能读,不能写!

  1. 复制原理:

Slave 启动成功连接到 master 后会发送一个sync同步命令

Master 接到命令,启动后台的存盘进程,同时收集所有接收到的用于修改数据集命令,在后台进程执行完毕之后,master将传送整个数据文件到slave,并完成一次完全同步。

**全量复制:**而slave服务在接收到数据库文件数据后,将其存盘并加载到内存中。

增量复制: Master 继续将新的所有收集到的修改命令依次传给slave,完成同步但是只要是重新连接master,一次完全同步(全量复制)将被自动执行! 我们的数据一定可以在从机中
看到!

  1. 层层链路
屏幕截图 2023-01-31 181832

这时候也可以完成我们的主从复制!

  1. 谋朝篡位

如果主机断开了连接,我们可以使用

1
SLAVEOF no one

让自己变成主机!其他的节点就可以手动连接到最新的这个主节点(手动)!如果这个时候原来的老大修复了,那就重新连接成为小弟!!

image-20230131181921153

老大没挂,也可以使用这个命令直接让自己变成老大!

主从复制原理

主从复制过程

​ 当一个 Redis 节点(slave 节点)接收到类似 slaveof 127.0.0.1 6379 的指令后直至其可以从 master 持续复制数据,大体经历了如下几个过程:

  1. 保存 master 地址

​ 当 slave 接收到 slaveof 指令后,slave 会立即将新的 master 的地址保存下来。

  1. 建立连接

​ slave 中维护着一个定时任务,该定时任务会尝试着与该 master 建立 socket 连接。如果连接无法建立,则其会不断定时重试,直到连接成功或接收到 slaveof no one 指令。

  1. slave 发送 ping 命令

​ 连接建立成功后,slave 会发送 ping 命令进行首次通信。如果 slave 没有收到 master 的回复,则 slave 会主动断开连接,下次的定时任务会重新尝试连接。

  1. 对 slave 身份验证

​ 如果 master 收到了 slave 的 ping 命令,并不会立即对其进行回复,而是会先进行身份验证。如果验证失败,则会发送消息拒绝连接;如果验证成功,则向 slave 发送连接成功响应。

  1. master 持久化

​ 首次通信成功后,slave 会向 master 发送数据同步请求。当 master 接收到请求后,会 fork出一个子进程,让子进程以异步方式立即进行持久化。

  1. 数据发送

​ 持久化完毕后 master 会再 fork 出一个子进程,让该子进程以异步方式将数据发送给slave。slave 会将接收到的数据不断写入到本地的持久化文件中。

​ 在 slave 数据同步过程中,master 的主进程仍在不断地接受着客户端的写操作,且不仅将新的数据写入到了 master 内存,同时也写入到了同步缓存。当 master 的持久化文件中的数据发送完毕后,master 会再将同步缓存中新的数据发送给 slave,由 slave 将其写入到本地持久化文件中。数据同步完成。

  1. slave 恢复内存数据

​ 当 slave 与 master 的数据同步完成后,slave 就会读取本地的持久化文件,将其恢复到本地内存,然后就可以对外提供读服务了。

  1. 持续增量复制

​ 在 slave 对外提供服务过程中,master 会持续不断的将新的数据以增量方式发送给 slave,以保证主从数据的一致性。

数据同步演变过程

  1. sync 同步

​ Redis 2.8 版本之前,首次通信成功后,slave 会向 master 发送 sync 数据同步请求。然后master 就会将其所有数据全部发送给 slave,由 slave 保存到其本地的持久化文件中。这个过程称为全量复制。

​ 但这里存在一个问题:在全量复制过程中可能会出现由于网络抖动而导致复制过程中断。当网络恢复后,slave 与 master 重新连接成功,此时 slave 会重新发送 sync 请求,然后会从头开始全量复制。

​ 由于全量复制过程非常耗时,所以期间出现网络抖动的概率很高。而中断后的从头开始不仅需要消耗大量的系统资源、网络带宽,而且可能会出现长时间无法完成全量复制的情况。

  1. psync 同步

​ Redis 2.8 版本之后,全量复制采用了 psync(Partial Sync,不完全同步)同步策略。当全量复制过程出现由于网络抖动而导致复制过程中断时,当重新连接成功后,复制过程可以**“断点续传”**。即从断开位置开始继续复制,而不用从头再来。这就大大提升了性能。

​ 为了实现 psync,整个系统做了三个大的变化:

A. 复制偏移量

​ 系统为每个要传送数据进行了编号,该编号从 0 开始,每个字节一个编号。该编号称为复制偏移量。参与复制的主从节点都会维护该复制偏移量。

image-20250508125924344

​ master 每发送过一个字节数据后就会进行累计。统计信息通过 info replication 的master_repl_offset 可查看到。同时,slave 会定时向 master 上报其自身已完成的复制偏移量给 master,所以 master 也会保存 slave 的复制偏移量 offset。

image-20250508125958331

​ slave 在接收到 master 的数据后,也会累计接收到的偏移量。统计信息通过 info replication的 slave_repl_offset 可查看到。

B. 主节点复制 ID

​ 当 master 启动后就会动态生成一个长度为 40 位的 16 进制字符串作为当前 master 的复制 ID,该 ID 是在进行数据同步时 slave 识别 master 使用的。通过 info replication 的master_replid 属性可查看到该 ID。

C. 复制积压缓冲区

​ 当 master 有连接的 slave 时,在 master 中就会创建并维护一个队列 backlog,默认大小为 1MB,该队列称为复制积压缓冲区。master 接收到了写操作数据不仅会写入到 master 主存,写入到 master 中为每个 slave 配置的发送缓存,而且还会写入到复制积压缓冲区。其作用就是用于保存最近操作的数据,以备“断点续传”时做数据补偿,防止数据丢失。

D. psync 同步过程

​ psync 是一个由 slave 提交的命令,其格式为 psync <master_replid> <repl_offset>,表示当前 slave 要从指定的 master 中的 repl_offset+1 处开始复制。repl_offset 表示当前 slave 已经完成复制的数据的 offset。该命令保证了“断点续传”的实现。

​ 在第一次开始复制时,slave 并不知道 master 的动态 ID,并且一定是从头开始复制,所以其提交的 psync 命令为 PSYNC ? -1。即 master_replid 为问号(?),repl_offset 为-1。

​ 如果复制过程中断后 slave 与 master 成功连接,则 slave 再次提交 psyn 命令。此时的 psyn命令的 repl_offset 参数为其前面已经完成复制的数据的偏移量。

​ 其实,并不是 slave 提交了 psyn 命令后就可以立即从 master 处开始复制,而是需要 master给出响应结果后,根据响应结果来执行。master 根据 slave 提交的请求及 master 自身情况会给出不同的响应结果。响应结果有三种可能:

  • FULLRESYNC <master_replid> <repl_offset>:告知 slave 当前 master 的动态 ID 及可以开始全量复制了,这里的 repl_offset 一般为 0
  • CONTINUE:告知 slave 可以按照你提交的 repl_offset 后面位置开始“续传”了
  • ERR:告知 slave,当前 master 的版本低于 Redis 2.8,不支持 psyn,你可以开始全量复制了

E. psync 存在的问题

  • 在 psync 数据同步过程中,若 slave 重启,在 slave 内存中保存的 master 的动态 ID 与续传 offset 都会消失,"断点续传"将无法进行,从而只能进行全量复制,导致资源浪费。
  • 在 psync 数据同步过程中,master 宕机后 slave 会发生“易主”,从而导致 slave 需要从新 master 进行全量复制,形成资源浪费。
  1. psync 同步的改进

​ Redis 4.0 对 psync 进行了改进,提出了"同源增量同步"策略。

A. 解决 slave 重启问题

​ 针对“slave 重启时 master 动态 ID 丢失问题”,改进后的 psync 将 master 的动态 ID 直接写入到了 slave 的持久化文件中。

​ slave 重启后直接从本地持久化文件中读取 master 的动态 ID,然后向 master 提交获取复制偏移量的请求。master 会根据提交请求的 slave 地址,查找到保存在 master 中的复制偏移量,然后向 slave 回复 FULLRESYNC <master_replid> <repl_offset>,以告知 slave 其马上要开始发送的位置。然后 master 开始"断点续传"。

B. 解决 slave 易主问题

​ slave 易主后需要和新 master 进行全量复制,本质原因是新 master 不认识 slave 提交的psync 请求中"原 master 的动态 ID"。如果 slave 发送 PSYNC <原 master_replid> <repl_offset>命令,新 master 能够识别出该 slave 要从原 master 复制数据,而自己的数据也都是从该 master复制来的。那么新 master 就会明白,其与该 slave"师出同门",应该接收其"断点续传"同步请求。

​ 而新 master 中恰好保存的有"原 master 的动态 ID"。由于改进后的 psync 中每个 slave都在本地保存了当前 master 的动态 ID,所以当 slave 晋升为新的 master 后,其本地仍保存有之前 master 的动态 ID。而这一点也恰恰为解决"slave 易主"问题提供了条件。通过 master的 info replicaton 中的 master_replid2 可查看到。如果尚未发生过易主,则该值为 40 个 0。

  1. 无盘操作

​ Redis 6.0 对同步过程又进行了改进,提出了"无盘全量同步"与"无盘加载"策略,避免了耗时的 IO 操作。

  • 无盘全量同步:master 的主进程 fork 出的子进程直接将内存中的数据发送给 slave,无需经过磁盘。
  • 无盘加载:slave 在接收到 master 发送来的数据后不需要将其写入到磁盘文件,而是直接写入到内存,这样 slave 就可快速完成数据恢复。
  1. 共享复制积压缓冲区

​ Redis 7.0 版本对复制积压缓冲区进行了改进,让各个 slave 的发送缓冲区共享复制积压缓冲区。这使得复制积压缓冲区的作用,除了可以保障数据的安全性外,还作为所有 slave的发送缓冲区,充分利用了复制积压缓冲区。

总结

一般来说,要将Redis运用于工程项目中,只使用一台Redis是万万不能的(宕机),原因如下:

  1. 从结构上,单个Redis服务器会发生单点故障,并且一台服务器需要处理所有的请求负载,压力较大;

  2. 从容量上,单个Redis服务器内存容量有限,就算一Redis服务器内存容量为256G,也不能将所有内存用作Redis存储内存,一般来说,单台Redis最大使用内存不应该20G。

主从复制,读写分离! 80% 的情况下都是在进行读操作!减缓服务器的压力!架构中经常使用! 一主二从!

只要在公司中,主从复制就是必须要使用的,因为在真实的项目中不可能单机使用Redis!