redis--主从复制

[TOC]

1. 主从赋值

从节点slave实例能精确得复制主节点master实例的内容。每次当slave和master断开时,slave会自动重连到master上,并且无论这期间master发生了什么,slave都会尝试将自己成为master的精确副本。

主从赋值的机制:

  • master和slave正常连接时:master会发送一连串的命令流来保持对slave的更新,以便于将自身的数据集复制给slave。包括客户端的写入、key的过期与丢弃等等。
  • master和slave断开,重连后:slave重新连接上master后,会尝试进行部分重同步。slave只会尝试获取在断开连接期间内丢失的命令流。
  • 无法进行重同步:如果无法进行重同步,那么slave就请求进行全量重同步。master需要创建所有数据的快照,将之发送给slave,之后在数据集更改时,持续发送命令流到slave。

redis使用默认的异步复制,具有低延迟和高性能的特点,也是大多数的自然复制模式。slave会异步确认master周期性发送的命令流(数据流)。

客户端可以使用WAIT命令请求同步复制某些特定的数据。但是WAIT命令只能确保在slave中存在指定数据的已确认副本。虽然slave已确认了副本存在,但是slave在持久化副本的时候,还是会受到slave自己的持久化策略的影响,最终仍然会因持久化策略,导致同步期间的数据丢失。

image-20200723204432316

2. 主从复制的特点

对于主从复制来说,一个master可能存在多个slave,随着slave的数量的增加,慢慢的master给slave发送数据流,就成为了master性能下降的原因之一了。

所以在redis中,有以下功能来保证主从复制:

  • 一个master可以有很多的slave
  • redis使用异步复制,slave和master之间可以异步确认处理的数据
  • ==slave可以接受其他slave的连接。==除了多个slave可以连接到同一个master之外,slave之间也可以以层叠结构连接到其他slave。在redis4.0开始,所有的slave会收到完全一样的赋值流。
  • 主从复制在master是非阻塞的,master可以在一个或多个slave进行初次同步或者是部分同步时,还可以继续处理查询请求。
  • 主从复制在slave大部分也是非阻塞的。当slave进行初次同步时,他可以使用旧数据集处理查询请求(需要在redis.conf中配置),否则slave会返回error给客户端。初次同步之后,旧的数据集会被删除,同时加载新的数据集,如果数据集很大,这个操作会造成slave短暂的卡顿,在卡顿期间阻塞。在redis4.0开始,可以配置slave删除旧的数据集在其他线程完成。但是加载新的数据集依然是主线程完成,依然会有小小的卡顿(非常不明显)。
  • 主从复制可以用在可伸缩性上,比如读多写少的场景,也可以作为备份机器使用(热备份,当主节点出现问题了,备份节点可以不做任何修改,在极其短的时间内成为主节点,实现主节点的操作)
  • 主从复制可以转移持久化,提高性能。比如slaveA是热备份机器,slaveB是持久化机器。slaveB从master或者slaveA得到完整的数据集,然后写入硬盘,因为master不需要做硬盘持久化,所以在一定的程度上可以提升性能。

3. 主从复制的安全

在第二小节中,我们谈到,利用主从赋值,可以将master的持久化转移给slave。要实现slave代替master进行持久化,就需要master关闭持久化。

但是master关闭持久化会存在非常严重的隐患:

假设在主从赋值的过程中,master重启了,因为master没有配置持久化,而且也没有从slave获取持久化文件。那么重启后的master就是一个没有任何数据的redis实例。slave重新和master连接后,并不知道master发生了重启,于是进行部分同步,所以slave也将全部的数据删除,成为了空数据的redis实例。将空数据持久化到硬盘,同时删除旧的持久化文件,此时就造成了数据全部丢失的严重问题。

如何解决:

关闭自动重启机制,当master因各种原因宕机,不应该立即重启,而是要从slave拿到持久化文件,然后在启动,让重新后的master根据持久化文件进行重建数据。

4. 主从复制的原理

每一个 master 都有一个 replication ID:这是一个较大的伪随机字符串,标记了一个给定的数据集。每个 master 也持有一个偏移量,master 将自己产生的复制流发送给 slave 时,发送多少个字节的数据,自身的偏移量就会增加多少,目的是当有新的操作修改自己的数据集时,它可以以此更新 slave 的状态。复制偏移量即使在没有一个 slave 连接到 master 时,也会自增,所以基本上每一对给定的

Replication ID, offset

都会标识一个 master 数据集的确切版本。

当 slave 连接到 master 时,它们使用 PSYNC命令来发送它们记录的旧的 master replication ID 和它们至今为止处理的偏移量。通过这种方式, master 能够仅发送 slave 所需的增量部分。但是如果 master 的缓冲区中没有足够的命令积压缓冲记录,或者如果 slave 引用了不再知道的历史记录(replication ID),则会转而进行一个全量重同步:在这种情况下, slave 会得到一个完整的数据集副本,从头开始。

下面是一个全量同步的工作细节:

master 开启一个后台保存进程,以便于生产一个 RDB 文件。同时它开始缓冲所有从客户端接收到的新的写入命令。当后台保存完成时, master 将数据集文件传输给 slave, slave将之保存在磁盘上,然后加载文件到内存。再然后 master 会发送所有缓冲的命令发给 slave。这个过程以指令流的形式完成并且和 Redis 协议本身的格式相同。

你可以用 telnet 自己进行尝试。在服务器正在做一些工作的同时连接到 Redis 端口并发出 SYNC 命令。你将会看到一个批量传输,并且之后每一个 master 接收到的命令都将在 telnet 回话中被重新发出。事实上 SYNC 是一个旧协议,在新的 Redis 实例中已经不再被使用,但是其仍然向后兼容:但它不允许部分重同步,所以现在 PSYNC 被用来替代SYNC

之前说过,当主从之间的连接因为一些原因崩溃之后, slave 能够自动重连。如果 master 收到了多个 slave 要求同步的请求,它会执行一个单独的后台保存,以便于为多个 slave 服务。

5. 无需磁盘参与的复制

正常情况下,一个全量重同步要求在磁盘上创建一个 RDB 文件,然后将它从磁盘加载进内存,然后 slave以此进行数据同步。

如果磁盘性能很低的话,这对 master 是一个压力很大的操作。Redis 2.8.18 是第一个支持无磁盘复制的版本。在此设置中,子进程直接发送 RDB 文件给 slave,无需使用磁盘作为中间储存介质。

image-20200723193240463

6. 配置主从复制

6.1 配置文件

slaveof host port

将这个命令加入redis.conf文件中即可使得redis实例成为从节点,host的redis实例是主节点。

image-20200723193155368

6.2 配置命令

在从节点的redis实例中使用slaveof命令,也会将当前节点设置为目标节点的slave,不过重启后会被redis.conf的配置重置。

小例子:

image-20200723200909704

将slave设置为master的从节点。

image-20200723200958915

master增加数据

image-20200723201329725

然后在slave查看,是否进行复制

image-20200723201358490

发现没有进行主从复制,是时间没到吗?那么就使用WAIT命令强制进行主从复制了

image-20200723201444835

一直没有进行主从复制。是打开方式不对吗,还是哪里出错了?

仔细回忆,slave的配置文件中有这样一句:

image-20200723201600779

应该是这个配置导致的,我们注释掉这个配置,然后重启

重新设置slave

image-20200723201934172

但是我又失败了。。。嗯嗯

image-20200723202003339

配置中的protectted-mode是和bind配合使用的吧

在试

image-20200723203242961

因为我在win 10系统上启动的redis-server,而且前面就将redis-server注册到操作系统的服务中心了

image-20200723203357583

使用redis-server --service-insttall config-path即可将redis-server注册。

使用redis-server --service-stop 停止

使用redis-server --service-start启动

使用redis-server --service-resart重启

重启了

image-20200723203612497

然后发现还是没有同步,看下日志

image-20200723203701365

嗯,应该是版本问题造成的:

image-20200723204015103

那么如果我本地在启动一个呢

image-20200723204111803

我又在本地启动了一个,端口是6301.

6301将是master,6300是slave

image-20200723204319523

成功实现。我们在6301中是没有做任何操作的,但是6300已经拥有了6301中的数据了。

7. slave只读

自从 Redis 2.6 之后, slave 支持只读模式且默认开启。redis.conf 文件中的 slave-read-only 变量控制这个行为,且可以在运行时使用 CONFIG SET 来随时开启或者关闭。

image-20200723194331071

只读模式下的 slave 将会拒绝所有写入命令,因此实践中不可能由于某种出错而将数据写入 slave 。

由于slave不会将数据传播到与该实例相连的slave上,slave总是接收与顶层redis实例发送的数据流。

graph LR
A --> B 
B --> C

对于C来说,C只会接收和A完全相同的数据流。

如果数据流在B中发生了修改,那么C是不接收的。

8. slave 验证

redis实例是可以配置密码的安全验证的。

所以,如果slave需要连接到master,需要通过验证才行。

需要在slave的配置文件中配置

masterauth

或者在命令行中使用

config set masterautn 进行配置

9. slave 数量控制

从Redis 2.8开始,只有当至少有 N 个 slave 连接到 master 时,才有可能配置 Redis master 接受写查询。

但是,由于 Redis 使用异步复制,因此无法确保 slave 是否实际接收到给定的写命令,因此总会有一个数据丢失窗口。

以下是该特性的工作原理:

  • slave 每秒钟都会 ping master,确认已处理的复制流的数量。
  • master 会记得上一次从每个 slave 都收到 ping 的时间。
  • 用户可以配置一个最小的 slave 数量,使得它滞后 <= 最大秒数。

如果至少有 N 个 slave ,并且滞后小于 M 秒,则写入将被接受。

你可能认为这是一个尽力而为的数据安全机制,对于给定的写入来说,不能保证一致性,但至少数据丢失的时间窗限制在给定的秒数内。一般来说,绑定的数据丢失比不绑定的更好。

如果条件不满足,master 将会回复一个 error 并且写入将不被接受。

这个特性有两个配置参数:

  • min-slaves-to-write
  • min-slaves-max-lag <秒数>
image-20200723200029991

10. 主从复制处理过期数据

Redis 的过期机制可以限制 key 的生存时间。此功能取决于 Redis 实例计算时间的能力,但是,即使使用 Lua 脚本更改了这些 key,slaves 也能正确地复制具有过期时间的 key。

为了实现这样的功能,Redis 不能依靠主从使用同步时钟,因为这是一个无法解决的并且会导致 race condition 和数据集不一致的问题,所以 Redis 使用三种主要的技术使过期的 key 的复制能够正确工作:

  • slave 不会让 key 过期,而是等待 master 让 key 过期。当一个 master 让一个 key 到期(或由于 LRU 算法将之驱逐)时,它会合成一个 DEL 命令并传输到所有的 slave。
  • 但是,由于这是 master 驱动的 key 过期行为,master 无法及时提供 DEL 命令,所以有时候 slave 的内存中仍然可能存在在逻辑上已经过期的 key 。为了处理这个问题,slave 使用它的逻辑时钟以报告只有在不违反数据集的一致性的读取操作(从主机的新命令到达)中才存在 key。用这种方法,slave 避免报告逻辑过期的 key 仍然存在。在实际应用中,使用 slave 程序进行缩放的 HTML 碎片缓存,将避免返回已经比期望的时间更早的数据项。
  • 在Lua脚本执行期间,不执行任何 key 过期操作。当一个Lua脚本运行时,从概念上讲,master 中的时间是被冻结的,这样脚本运行的时候,一个给定的键要么存在要么不存在。这可以防止 key 在脚本中间过期,保证将相同的脚本发送到 slave ,从而在二者的数据集中产生相同的效果。

一旦一个 slave 被提升为一个 master ,它将开始独立地过期 key,而不需要任何旧 master 的帮助。

如果主从复制中主节点和从节点处理过期数据不及时,会造成==主节点与读节点读取数据不一致==的问题。

11. 主从复制信息查询

有两个 Redis 命令可以提供有关主从实例当前复制参数的很多信息。一个是INFO。如果使用复制参数像 INFO replication 调用该命令,,则只显示与复制相关的信息。另一个更加 友好 的命令是 ROLE,它提供 master 和 slave 的复制状态以及它们的复制偏移量,连接的 slaves 列表等等。

你可能感兴趣的:(redis--主从复制)