[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自己的持久化策略的影响,最终仍然会因持久化策略,导致同步期间的数据丢失。
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,无需使用磁盘作为中间储存介质。
6. 配置主从复制
6.1 配置文件
slaveof host port
将这个命令加入redis.conf文件中即可使得redis实例成为从节点,host的redis实例是主节点。
6.2 配置命令
在从节点的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中的数据了。
7. slave只读
自从 Redis 2.6 之后, slave 支持只读模式且默认开启。redis.conf 文件中的 slave-read-only 变量控制这个行为,且可以在运行时使用 CONFIG SET 来随时开启或者关闭。
只读模式下的 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 <秒数>
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 列表等等。