redis_主从复制(Replication)
Master:主 Slaves:从
Redis 支持简单且易用的主从复制(master-slave replication)功能, 该功能可以让从服务器(slave server)成为主服务器(master server)的精确复制品。
1、以下是关于 Redis 复制功能的几个重要方面:
1.1、Redis 使用异步复制。 从 Redis 2.8 开始, 从服务器会以每秒一次的频率向主服务器报告复制流(replication stream)的处理进度。
1.2、一个主服务器可以有多个从服务器。
1.3、不仅主服务器可以有从服务器, 从服务器也可以有自己的从服务器, 多个从服务器之间可以构成一个图状结构。
1.4、复制功能不会阻塞主服务器: 即使有一个或多个从服务器正在进行初次同步, 主服务器也可以继续处理命令请求。
1.5、复制功能也不会阻塞从服务器: 只要在 redis.conf 文件中进行了相应的设置, 即使从服务器正在进行初次同步, 服务器也可以使用旧版本的数据集来处理命令查询。
不过, 在从服务器删除旧版本数据集并载入新版本数据集的那段时间内, 连接请求会被阻塞。
你还可以配置从服务器, 让它在与主服务器之间的连接断开时, 向客户端发送一个错误。
1.6、复制功能可以单纯地用于数据冗余(data redundancy), 也可以通过让多个从服务器处理只读命令请求来提升扩展性(scalability): 比如说, 繁重的 SORT key [BY pattern] [LIMIT offset count] [GET pattern [GET pattern …]] [ASC | DESC] [ALPHA] [STORE destination] 命令可以交给附属节点去运行。
1.7、可以通过复制功能来让主服务器免于执行持久化操作: 只要关闭主服务器的持久化功能, 然后由从服务器去执行持久化操作即可。
2、关闭主服务器持久化时,复制功能的数据存在安全问题
当配置Redis复制功能时,强烈建议打开主服务器的持久化功能。 否则的话,由于延迟等问题,部署的服务应该要避免自动拉起。
为了帮助理解主服务器关闭持久化时自动拉起的危险性,参考一下以下会导致主从服务器数据全部丢失的例子:
2.1. 假设节点A为主服务器,并且关闭了持久化。 并且节点B和节点C从节点A复制数据
2.2. 节点A崩溃,然后由自动拉起服务重启了节点A. 由于节点A的持久化被关闭了,所以重启之后没有任何数据
2.3. 节点B和节点C将从节点A复制数据,但是A的数据是空的, 于是就把自身保存的数据副本删除。
在关闭主服务器上的持久化,并同时开启自动拉起进程的情况下,即便使用Sentinel来实现Redis的高可用性,也是非常危险的。 因为主服务器可能拉起得非常快,以至于Sentinel在配置的心跳时间间隔内没有检测到主服务器已被重启,然后还是会执行上面的数据丢失的流程。
无论何时,数据安全都是极其重要的,所以应该禁止主服务器关闭持久化的同时自动拉起。
3、主从复制功能的运作原理
无论是初次连接还是重新连接, 当建立一个从服务器时, 从服务器都将向主服务器发送一个 SYNC 命令。
接到 SYNC 命令的主服务器将开始执行 BGSAVE , 并在保存操作执行期间, 将所有新执行的写入命令都保存到一个缓冲区里面。
当 BGSAVE 执行完毕后, 主服务器将执行保存操作所得的 .rdb 文件发送给从服务器, 从服务器接收这个 .rdb 文件, 并将文件中的数据载入到内存中。
之后主服务器会以 Redis 命令协议的格式, 将写命令缓冲区中积累的所有内容都发送给从服务器。
你可以通过 telnet 命令来亲自验证这个同步过程: 首先连上一个正在处理命令请求的 Redis 服务器, 然后向它发送 SYNC 命令, 过一阵子, 你将看到 telnet 会话(session)接收到服务器发来的大段数据(.rdb 文件), 之后还会看到, 所有在服务器执行过的写命令, 都会重新发送到 telnet 会话来。
即使有多个从服务器同时向主服务器发送 SYNC , 主服务器也只需执行一次 BGSAVE 命令, 就可以处理所有这些从服务器的同步请求。
从服务器可以在主从服务器之间的连接断开时进行自动重连, 在 Redis 2.8 版本之前, 断线之后重连的从服务器总要执行一次完整重同步(full resynchronization)操作, 但是从 Redis 2.8 版本开始, 从服务器可以根据主服务器的情况来选择执行完整重同步还是部分重同步(partial resynchronization)。
4、部分重同步
从 Redis 2.8 开始, 在网络连接短暂性失效之后, 主从服务器可以尝试继续执行原有的复制进程(process), 而不一定要执行完整重同步操作。
这个特性需要主服务器为被发送的复制流创建一个内存缓冲区(in-memory backlog), 并且主服务器和所有从服务器之间都记录一个复制偏移量(replication offset)和一个主服务器 ID (master run id), 当出现网络连接断开时, 从服务器会重新连接, 并且向主服务器请求继续执行原来的复制进程:
如果从服务器记录的主服务器 ID 和当前要连接的主服务器的 ID 相同, 并且从服务器记录的偏移量所指定的数据仍然保存在主服务器的复制流缓冲区里面, 那么主服务器会向从服务器发送断线时缺失的那部分数据, 然后复制工作可以继续执行。
否则的话, 从服务器就要执行完整重同步操作。
Redis 2.8 的这个部分重同步特性会用到一个新增的 PSYNC master_run_id offset 内部命令, 而 Redis 2.8 以前的旧版本只有 SYNC 命令, 不过, 只要从服务器是 Redis 2.8 或以上的版本, 它就会根据主服务器的版本来决定到底是使用 PSYNC master_run_id offset 还是 SYNC :
如果主服务器是 Redis 2.8 或以上版本,那么从服务器使用 PSYNC master_run_id offset 命令来进行同步。
如果主服务器是 Redis 2.8 之前的版本,那么从服务器使用 SYNC 命令来进行同步。
5、如何配置
配置一个从服务器非常简单, 只要在配置文件中增加以下的这一行就可以了:
slaveof 192.168.1.1 6379
当然, 你需要将代码中的 192.168.1.1 和 6379 替换成你的主服务器的 IP 和端口号。
另外一种方法是调用 SLAVEOF host port 命令, 输入主服务器的 IP 和端口, 然后同步就会开始:
[root@localhost redis7012]# ./redis-cli -c -h 192.168.20.112 -p 7012
192.168.20.112:7012> slaveof 192.168.20.112 7011
OK
6、只读从服务器
从 Redis 2.6 开始, 从服务器支持只读模式, 并且该模式为从服务器的默认模式。
只读模式由 redis.conf 文件中的 slave-read-only 选项控制, 也可以通过 CONFIG SET parameter value 命令来开启或关闭这个模式。
只读从服务器会拒绝执行任何写命令, 所以不会出现因为操作失误而将数据不小心写入到了从服务器的情况。
即使从服务器是只读的, DEBUG 和 CONFIG 等管理式命令仍然是可以使用的, 所以我们还是不应该将服务器暴露给互联网或者任何不可信网络。 不过, 使用 redis.conf 中的命令改名选项, 我们可以通过禁止执行某些命令来提升只读从服务器的安全性。
你可能会感到好奇, 既然从服务器上的写数据会被重同步数据覆盖, 也可能在从服务器重启时丢失, 那么为什么要让一个从服务器变得可写呢?
原因是, 一些不重要的临时数据, 仍然是可以保存在从服务器上面的。 比如说, 客户端可以在从服务器上保存主服务器的可达性(reachability)信息, 从而实现故障转移(failover)策略。
7、从服务器相关配置
如果主服务器通过 requirepass 选项设置了密码, 那么为了让从服务器的同步操作可以顺利进行, 我们也必须为从服务器进行相应的身份验证设置。
对于一个正在运行的服务器, 可以使用客户端输入以下命令:
config set masterauth <password>
要永久地设置这个密码, 那么可以将它加入到配置文件中:
masterauth <password>
另外还有几个选项, 它们和主服务器执行部分重同步时所使用的复制流缓冲区有关, 详细的信息可以参考 Redis 的配置 redis.conf 文件介绍。
8、主服务器只在有至少 N 个从服务器的情况下,才执行写操作
从 Redis 2.8 开始, 为了保证数据的安全性, 可以通过配置, 让主服务器只在有至少 N 个当前已连接从服务器的情况下, 才执行写命令。
不过, 因为 Redis 使用异步复制, 所以主服务器发送的写数据并不一定会被从服务器接收到, 因此, 数据丢失的可能性仍然是存在的。
以下是这个特性的运作原理:
从服务器以每秒一次的频率 PING 主服务器一次, 并报告复制流的处理情况。
主服务器会记录各个从服务器最后一次向它发送 PING 的时间。
用户可以通过配置, 指定网络延迟的最大值 min-slaves-max-lag , 以及执行写操作所需的至少从服务器数量 min-slaves-to-write 。
如果至少有 min-slaves-to-write 个从服务器, 并且这些服务器的延迟值都少于 min-slaves-max-lag 秒, 那么主服务器就会执行客户端请求的写操作。
你可以将这个特性看作 CAP 理论中的 C 的条件放宽版本: 尽管不能保证写操作的持久性, 但起码丢失数据的窗口会被严格限制在指定的秒数中。
另一方面, 如果条件达不到 min-slaves-to-write 和 min-slaves-max-lag 所指定的条件, 那么写操作就不会被执行, 主服务器会向请求执行写操作的客户端返回一个错误。
以下是这个特性的两个选项和它们所需的参数:
min-slaves-to-write <number of slaves>
min-slaves-max-lag <number of seconds>
详细的信息可以参考 Redis 源码中附带的 redis.conf 示例文件。
以上资料摘自自于redis中文网,仅供自我学习使用。
9、应用实例(Master-Slave已经建立)
主服务器:
192.168.20.112:7011> get keys
(nil)
192.168.20.112:7011> set mykey hello
OK
192.168.20.112:7011> set mykey2 world
OK
192.168.20.112:7011> keys *
1) "mykey"
2) "mykey2"
3) "mylist"
192.168.20.112:7011> del mykey2
(integer) 1
192.168.20.112:7011> keys *
1) "mykey"
2) "mylist"
192.168.20.112:7011> set key my 555
(error) ERR syntax error
192.168.20.112:7011> set my 88
OK
192.168.20.112:7011> keys *
1) "my"
2) "mykey"
3) "mylist"
从服务器1:
192.168.20.112:7012> get keys
(nil)
192.168.20.112:7012> keys *
1) "mykey2"
2) "mylist"
3) "mykey"
192.168.20.112:7012> keys *
1) "mylist"
2) "mykey"
192.168.20.112:7012> set key my
(error) READONLY You can't write against a read only slave.
192.168.20.112:7012> set my 88 // 设置key报错
(error) READONLY You can't write against a read only slave.
192.168.20.112:7012> keys *
1) "mylist"
2) "my"
3) "mykey"
从服务器2:
192.168.20.112:7013> get keys
(nil)
192.168.20.112:7013> keys *
1) "mylist"
2) "mykey2"
3) "mykey"
192.168.20.112:7013> keys *
1) "mylist"
2) "mykey"
192.168.20.112:7013> keys *
1) "mylist"
2) "my"
3) "mykey"
10、主服务器只接收数据,持久化操作在从服务器进行。可以减小主服务器的压力。但是不建议,修改主从配置即可,故此不再演示。
11、主从节点的缺点
master节点挂了以后,redis就不能对外提供写服务了,因为剩下的slave不能成为master
这个缺点影响是很大的,尤其是对生产环境来说,是一刻都不能停止服务的,所以一般的生产坏境是不会单单只有主从模式的。
12、心跳检测
主从节点在建立复制后,他们之间维护着长连接,并且彼此发送心跳.如图:
主从心跳判断机制:
12.1、主从节点彼此都有心跳检测机制,各自模拟成对方的客户端急性通信,通过 client list 命令查看复制相关客户端信息,主节点的连接状态为 flags = M,从节点的连接状态是 flags = S.
12.2、主节点默认每隔10秒对从节点发送ping命令,判断从节点的存活状态和连接状态,可通过修改配置 repl-ping-slave-period 控制发送频率.
12.3、从节点在主线程每隔1秒发送 replconf ack{offset} 命令,给主节点上报自身当前的复制偏移量.
12.4、主节点收到 replconf 信息后,判断从节点超时时间,如果超过 repl-timeout 设置的值(默认值为60 秒),则判断从节点下线,并断开复制客户端连接.
replconf的作用:
a:实时监测主从节点的网络状态
b:上报自身的偏移量,检查复制数据是否丢失
c:实现保证从节点的数量和延迟功能,通过min-slaves-to-write,min-slaves-max-lag参数配置定义
此外为了降低主从延迟,一般把 Redis 主从节点部署在相同的机房/同城机房,避免网络延迟带来的网络分区造成的心跳中断等情况。
13、主从复制存在的问题(读写分离、规避全量复制、规避复制风暴、主从配置不一致)
13.1:读写分离的一些问题
对于读占比较高的场景,可以通过把部分的读流量分摊到slave节点来减轻master节点的压力,同时需要注意master节点永远只执行写操作。
在使用slave节点响应读请求时,业务端可能会遇到一些问题,如:复制数据延迟、读到过期数据、slave节点故障。
1).数据延迟
Redis复制数据的延迟,是由于复制的一部特性导致的,因此无法避免。但是延迟主要是取决于网络带宽和命令阻塞的情况而定,比如master节点刚写入数据,在slave节点上是可能读取不到数据的。在大量延迟的场景下,可以编写外部程序监听主从节点的复制偏移量,延迟较大时发出报警或通知,实现方式如下:
对于具体延迟,监控程序可通过检查 info replication 的 offset 指标记录,从节点的偏移量可以查询主节点的offset指标,它们的差值就是主从延迟的字节量。
如果字节量过高,可以采用zookeeper的监听回调机制实现客户端通知。
客户端接收通知后,修改读命令路由到主节点或其他从节点上,当延迟恢复后,再通知客户端。
这种方案的成本比较高,需要单独修改适配Redis的客户端类库。如果涉及多种语言成本将会扩大。客户端逻辑需要识别出读写请求并自动路由,还需要维护故障和恢复的通知。采用此方案视具体的业务而定,如果允许不 一致性或对延迟不敏感的业务可以忽略,也可以采用Redis集群方案做水平扩展。
2).读到过期数据
当master节点存储大量超时的数据,譬如缓存数据,Redis内部需要维护过期数据的删除策略。删除策略主要有两种:惰性删除和定时删除。
惰性删除:master节点每次读取命令时都会检查键是否超时,如果超时则执行del命令删除键对象,之后异步把del命令slave节点,这样可以保证数据复制的一致性,slave节点是永远不会主动去删除超时数据的。
定时删除:Redis的master节点在内部定时任务,会循环采样一定数量的键,当发现采样的键过期时,会执行del命令,之后再同步个slave节点。
注:如果数据大量超时,master节点采样速度跟不上过期的速度,而且master节点没有读取过期键的操作,那slave节点时无法收到del命令的,这时从节点上读取的数据已经时超时的了。但是Redis3.2版本中已经解决了这个问题,在此版本中slave节点读取数据之前会检查键过期时间来决定是否返回数据的。
3).从节点故障问题
对于slave节点故障问题,是需要在客户端维护可用的slave节点列表,当slave节点故障时,需立刻切换到其他从节点或主节点上。也可以通过 zookeeper 等协调者解决。
13.2:规避全量复制
全量复制时一个非常消耗资源的操作,因此避免全量复制是一个重要的关注点。全量复制通常有 3 种情况:第一次全量复制、节点运行 ID 不匹配、复制积压缓冲区不足。
1). 第一次全量复制
由于时第一次建立复制,从节点没有任何主节点的数据,因此必须进行全量复制才可以完成数据同步,对于这种情况的全量复制自然是无法避免的。当对数据量较大且流量较高的主节点从节点时,建议在低峰时操作,或者尽量规避使用大数据量的Redis节点。
·2). 节点运行ID不匹配
当主从复制关系建立后,从节点会保存主节点的运行ID,如果此时主节点出现故障重启,那么它的运行ID会改变。从节点发现运行ID不匹配后,会认为自己复制的是一个新的主节点,进而就回进行全量复制。对于这类情况,应该要从架构去规避,譬如提供故障转移功能。当主节点发生故障后,手动提升从节点为主节点或者采用支持故障转移的哨兵或集群来解决。
3). 复制积压缓冲区不足
在主从节点网络中断时后,当从节点再次连上主节点时,会发送psync {offset} {runId} 命令请求部分复制,如果请求的偏移量不在主节点积压缓冲区内,则无法提供给从节点数据,此时会使部分复制转变为全量复制。针对此类情况,需根据网络中断的时长,写命令数据量分析出合理的积压缓冲区的大小。写命令的数据量会根据主节点每秒的info replication的master_repl_offset差值获取(write_size_per_minute)。积压缓冲区的默认大小为1MB,在大流量的场景显然是不够的,这时需要修改 repl_backlog_size 配置,从而避免因复制积压缓冲区不足造成的全量复制。
13.3:规避复制风暴
复制风暴是指大量从节点对同一主节点或者同一台机器的多个主节点,在短时间内发起全量复制的过程。此时将导致被发起的主节点或机器产生大量开销,如 :CPU、内存、硬盘、带宽等。我们可以通过分析这样的复制场景,然后采用合理的方式进行规避。规避方式如下:
1).单节点复制风暴
单节点复制风暴,一般是发生在主节点挂在多个从节点的场景下。当主节点重启恢复后,从节点发起全量复制流程,此时主节点会为从节点创建RDB快照,如果在快照创建完毕之前,有多个从节点尝试与主节点进行全量同步,那么其他的从节点将共享这份RDB快照。这方面Redis做了相关优化,有效的避免了创建多个快照。但是同时像多个从节点发送快照,可能会使主节点的网络带宽消耗严重,造成主节点延迟变大,极端情况会出现主从断开,导致复制失败。
解决方案:首先减少主节点挂在从节点的数量,或者采用树状复制结构。
从节点采用树状树非常有用,网络开销交给位于中间层的从节点,而不必消耗顶层的主节点。但是这种树状结构也带来了运维的复杂性,增加了手动和自动处理故障转移的难度。
2). 单机复制风暴
由于 Redis 的单线程架构,通常会在一台物理机上部署多个Redis实例。如果这台机器出现故障或网络长时间中断,当他重启恢复后,会有大量从节点针对这台机器的主节点进行全量复制,会造成当前机器带宽耗尽。
解决方案:(1). 应当把主节点尽量分散在多台机器上,避免在单台机器上部署过多的主节点。
(2). 当主节点所在机器故障后提供故障恢复转移机制,避免机器恢复后进行密集的全量复制。
13.4:主从复制不一致
主从复制不一致是一个容易忽视的问题。对于有些配置可以不一样,比如:主节点关闭 AOF而从节点开启。
但对于内存方面的配置必须要一致,例如: maxmemory、hash-max-ziplist-entries 等参数,当配置的maxmemory从节点的内存小于主节点,如果复制的数据量超过了从节点的 maxmemory,他会根据淘汰策略(maxmemory-policy)进行内存溢出控制,
此时从节点数据已经丢失,但主从复制流程依然正常进行,复制偏移量也正常,但主从数据已经不一致。修复这类问题,也只能手动进行全量复制。压缩列表相关参数不一致时,虽然主从节点存 储的数据一致但实际内存占用情况差异会比较大。