从节点slave实例能精确得复制主节点master实例的内容。每次当slave和master断开时,slave会自动重连到master上,并且无论这期间master发生了什么,slave都会尝试将自己成为master的精确副本。
主从赋值的机制:
redis使用默认的异步复制,具有低延迟和高性能的特点,也是大多数的自然复制模式。slave会异步确认master周期性发送的命令流(数据流)。
客户端可以使用WAIT
命令请求同步复制某些特定的数据。但是WAIT
命令只能确保在slave中存在指定数据的已确认副本。虽然slave已确认了副本存在,但是slave在持久化副本的时候,还是会受到slave自己的持久化策略的影响,最终仍然会因持久化策略,导致同步期间的数据丢失。
对于主从复制来说,一个master可能存在多个slave,随着slave的数量的增加,慢慢的master给slave发送数据流,就成为了master性能下降的原因之一了。
所以在redis中,有以下功能来保证主从复制:
在第二小节中,我们谈到,利用主从赋值,可以将master的持久化转移给slave。要实现slave代替master进行持久化,就需要master关闭持久化。
但是master关闭持久化会存在非常严重的隐患:
假设在主从赋值的过程中,master重启了,因为master没有配置持久化,而且也没有从slave获取持久化文件。那么重启后的master就是一个没有任何数据的redis实例。slave重新和master连接后,并不知道master发生了重启,于是进行部分同步,所以slave也将全部的数据删除,成为了空数据的redis实例。将空数据持久化到硬盘,同时删除旧的持久化文件,此时就造成了数据全部丢失的严重问题。
如何解决:
关闭自动重启机制,当master因各种原因宕机,不应该立即重启,而是要从slave拿到持久化文件,然后在启动,让重新后的master根据持久化文件进行重建数据。
每一个 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 服务。
正常情况下,一个全量重同步要求在磁盘上创建一个 RDB 文件,然后将它从磁盘加载进内存,然后 slave以此进行数据同步。
如果磁盘性能很低的话,这对 master 是一个压力很大的操作。Redis 2.8.18 是第一个支持无磁盘复制的版本。在此设置中,子进程直接发送 RDB 文件给 slave,无需使用磁盘作为中间储存介质。
slaveof host port
将这个命令加入redis.conf文件中即可使得redis实例成为从节点,host的redis实例是主节点。
在从节点的redis实例中使用slaveof
命令,也会将当前节点设置为目标节点的slave,不过重启后会被redis.conf的配置重置。
小例子:
将slave设置为master的从节点。
master增加数据
然后在slave查看,是否进行复制
发现没有进行主从复制,是时间没到吗?那么就使用WAIT
命令强制进行主从复制了
一直没有进行主从复制。是打开方式不对吗,还是哪里出错了?
仔细回忆,slave的配置文件中有这样一句:
应该是这个配置导致的,我们注释掉这个配置,然后重启
重新设置slave
但是我又失败了。。。嗯嗯
配置中的protectted-mode是和bind配合使用的吧
在试
因为我在win 10系统上启动的redis-server,而且前面就将redis-server注册到操作系统的服务中心了
使用redis-server --service-insttall config-path
即可将redis-server注册。
使用redis-server --service-stop
停止
使用redis-server --service-start
启动
使用redis-server --service-resart
重启
重启了
然后发现还是没有同步,看下日志
嗯,应该是版本问题造成的:
那么如果我本地在启动一个呢
我又在本地启动了一个,端口是6301.
6301将是master,6300是slave
成功实现。我们在6301中是没有做任何操作的,但是6300已经拥有了6301中的数据了。
自从 Redis 2.6 之后, slave 支持只读模式且默认开启。redis.conf 文件中的 slave-read-only 变量控制这个行为,且可以在运行时使用 CONFIG SET 来随时开启或者关闭。
只读模式下的 slave 将会拒绝所有写入命令,因此实践中不可能由于某种出错而将数据写入 slave 。
由于slave不会将数据传播到与该实例相连的slave上,slave总是接收与顶层redis实例发送的数据流。
对于C来说,C只会接收和A完全相同的数据流。
如果数据流在B中发生了修改,那么C是不接收的。
redis实例是可以配置密码的安全验证的。
所以,如果slave需要连接到master,需要通过验证才行。
需要在slave的配置文件中配置
masterauth
或者在命令行中使用
config set masterautn
进行配置
从Redis 2.8开始,只有当至少有 N 个 slave 连接到 master 时,才有可能配置 Redis master 接受写查询。
但是,由于 Redis 使用异步复制,因此无法确保 slave 是否实际接收到给定的写命令,因此总会有一个数据丢失窗口。
以下是该特性的工作原理:
如果至少有 N 个 slave ,并且滞后小于 M 秒,则写入将被接受。
你可能认为这是一个尽力而为的数据安全机制,对于给定的写入来说,不能保证一致性,但至少数据丢失的时间窗限制在给定的秒数内。一般来说,绑定的数据丢失比不绑定的更好。
如果条件不满足,master 将会回复一个 error 并且写入将不被接受。
这个特性有两个配置参数:
Redis 的过期机制可以限制 key 的生存时间。此功能取决于 Redis 实例计算时间的能力,但是,即使使用 Lua 脚本更改了这些 key,slaves 也能正确地复制具有过期时间的 key。
为了实现这样的功能,Redis 不能依靠主从使用同步时钟,因为这是一个无法解决的并且会导致 race condition 和数据集不一致的问题,所以 Redis 使用三种主要的技术使过期的 key 的复制能够正确工作:
一旦一个 slave 被提升为一个 master ,它将开始独立地过期 key,而不需要任何旧 master 的帮助。
如果主从复制中主节点和从节点处理过期数据不及时,会造成主节点与读节点读取数据不一致的问题。
有两个 Redis 命令可以提供有关主从实例当前复制参数的很多信息。一个是INFO
。如果使用复制参数像 INFO replication
调用该命令,,则只显示与复制相关的信息。另一个更加 友好 的命令是 ROLE
,它提供 master 和 slave 的复制状态以及它们的复制偏移量,连接的 slaves 列表等等。