redis复制功能

引用文章:http://www.redis.cn/topics/replication.html

Redis官方网站:http://redis.io

基于Redis复制,使用和配置主从复制变得非常简单,同时也使得Redis slave节点能精确复制Redis master节点的内容。每当slave与master间的连接断开,slave都会自动重连master,无论断开期间master发生什么,slave都会尝试让自身成为master的精确副本。

这个系统运行依靠三个主要机制:

·当master与slave正常连接时,master会发送一系列命令流到slave节点,以此将自身数据集的改变应用于slave,如:客户端写入,key的过期与清除等

·master与slave间连接断开(如网络问题,主从连接超时),此后slave重新连接上master并会进行部分重同步,也就是说,他只会尝试同步断开连接期间丢失的命令流

·当无法进行部分重同步时,slave会请求进行全量重同步;master需创建所有数据的快照,将至发送给slave,之后在数据集更改时持续发送命令流到slave

Redis默认使用异步复制,特点:高延迟、高性能。

客户端可使用“WAIT”命令来请求同步复制某些特定数据。但,WAIT命令只能确保在其他Redis实例中有指定数量的已确认副本:在故障转移期间,由于不同原因的故障转移或是由于Redis持久性的实际配置,故障转移期间确认的写入操作可能仍然会丢失。你可以查看Sentinel或Redis集群文档,了解关于高可用性和故障转移的更多信息。本文的其余部分主要描述Redis基本复制功能的基本特性

以下是关于Redis 复制功能的几个特性:

·Redis使用异步复制。自Redis 2.8起,slave会以每秒一次的频率向master报告复制流(replication stream)的处理进度。

·一个master可拥有多个slave

·不仅master可以有从服务器,从服务器也可以有自己的从服务器,自Redis 4.0起,所有的sub-slave将会从master收到完全一样的复制流。

·主从复制功能不会阻塞master,意味着master与一个或多个slave进行初次同步或部分重同步时,也可以继续处理命令请求

·复制功能也不会阻塞slave,只要在redis.conf文件中进行了相应的设置,即使从服务器正在进行初次同步,服务器也可以使用旧版本的数据集来处理命令查询,同时也可以配置成这样:若slave与master间的复制流断开时,slave向客户端返回错误信息。不管怎么配置,在从服务器删除旧版本数据集并载入新版本数据集的那段时间内,连接请求会被阻塞,自Redis 4.0开始,可以配置Redis使删除旧数据集的操作在另一个不同的线程中进行,但是,加载新数据集的操作依然需要在主线程中进行并且会阻塞slave

·复制功能可用于数据冗余(data redundancy),也可通过配置让多个slave处理只读命令请求来提升扩展性(scalability)(如O(N)复杂度的慢操作可以被下放到slave)

·可以使用复制来避免master节点将数据写入磁盘造成的开销:配置master配置文件关闭Redis master的持久化功能,然后slave配置为不定期保存或启用AOF。但必须小心处理此配置,因为重新启动的master节点是空数据集,若slave视图与它同步,那么这个slave也会被清空

当master关闭持久化功能时,复制的安全性问题

配置Redis复制功能时,强烈建议在master和slave中都启用持久化。当不可能启用时(如磁盘读写性能差而导致的延时问题),应配置实例以避免服务器重新引导后Redis实例自动启动(避免启动空的master导致slave被清空)

可以尝试还原以下场景:

1、我们设置节点A为master并关闭它的持久化设置,节点B和C从节点A复制数据。

2、节点A崩溃,但是他有一些自动重启的系统可以重启进程。但是由于持久化被关闭了,节点重启后其数据集合为空。

3、节点B和节点C会从节点A复制数据,但是节点A的数据集是空的,因此复制的结果是它们会销毁自身之前的数据副本。

使用Redis 哨兵模式(sentinel)实现高可用,且配置master的持久化功能为关闭,此时若允许自动重启进程也很危险,如master可以重启的足够快以至于sentinel没有探测到故障,因此上述场景也可能发生。

Redis复制功能工作原理

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

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

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

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

可以使用telnet进行尝试,在服务器正在做一些工作的同时,连接到redis端口并发出“SYNC”命令,将会看到一个批量传输情况。且之后每个master接收到的命令都将在telnet会话中被重新发出。实际上“SYNC”是一个旧协议,在新的redis版本中已经不再被使用,但仍然向后兼容,但它并不允许部分重同步,替换命令是“PSYNC

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

无盘复制

一般一个全量重同步要求在磁盘上创建RDB文件,master发送给slave,slave从磁盘加载进内存,然后slave以此数据为基本进行数据同步。如果磁盘性能很低的话,这对master 是一个压力很大的操作。Redis 2.8.18 是第一个支持无磁盘复制的版本。在此设置中,子进程直接发送RDB文件给slave,无需使用磁盘作为中间储存介质

主从复制相关配置

启用Redis slave的复制功能:

slaveofmasterIP/masterDOMAIN_NAMEmasterPORT

指定master的IP/域名、端口

masterauth master-password

指定master的认证秘钥,默认没有

slave-read-only yes

slave只读模式,将短暂数据存储在writable slave中还是有一些合理的用例的。如,计算slow Set或者Sorted Set的操作并将它们存储在本地key中是多次观察到的使用writable slave的用例。

但注意,4.0 版本之前的writable slaves不能用TTL来淘汰key。这意味着,如果你使用EXPIRE或者其他命令为key设置了最大TTL,你将会在键值计数(count of keys)中看到这个key,且它还在#内存中。所以总的来说,将writable slaves和设置过TTL的key混用将会导致问题。Redis 4.0 RC3及更高版本彻底解决了这个问题,现在writable slaves能够像master一样清除TTL过期的key了,#但DB编号大于63(默认Redis实例只有16个数据库)的key除外。

另请注意,由于Redis 4.0 writable slaves 仅能本地,并且不会将数据传播到与该实例相连的sub-slave上。sub-slave将总是接收与最顶层master向intermediate slaves发送的复制流相同的复制流。#所以例如在以下设置中:

A —> B—> C

即使节点B是可写的,C也不会看到B的写入,而是将拥有和master实例A相同的数据集

repl-diskless-sync no/yes

新的从节点和重连后不能继续备份的从节点,需要做所谓的“完全备份”,即将一个RDB文件从主节点传送到从节点。这个传送有以下两种方式:

1)硬盘备份:redis主节点创建一个新的进程,用于把RDB文件写到硬盘上。过一会儿,其父进程递增地将文件传送给从节点。

2)无硬盘备份:redis主节点创建一个新的进程,子进程直接把RDB文件写到从节点的套接字,不需要用到硬盘。

在硬盘备份的情况下,主节点的子进程生成RDB文件。一旦生成,多个从节点可以立即排成队列使用主节点的RDB文件。在无硬盘备份的情况下,一次RDB传送开始,新的从节点到达后,需要等#待现在的传送结束,才能开启新的传送。

若使用无硬盘备份,主节点会在开始传送之间等待一段时间(可配置,以秒为单位),希望等待多个子站到达后并行传送。

在硬盘低速而网络高速(高带宽)情况下,无硬盘备份更好。

repl-diskless-sync-delay 5

当启用无硬盘备份,服务器等待一段时间后才会通过套接字向从节点传送RDB文件,这个等待时间是可配置的。这一点很重要,因为一旦传送开始,就不可能再为一个新到达的从节点服务。从节点#则要排队等待下一次RDB传送。因此服务器等待一段时间以期更多的从节点到达。延迟时间以秒为单位,默认为5秒。要关掉这一功能,只需将它设置为秒,传送会立即启动

slave-serve-stale-data yes

当一个从节点失去与主节点的连接,或者正在与主节点同步,从节点可以有以下两种行为:

1)若slave-serve-stale-data设置为yes(默认),从节点会继续响应客户端的请求,有可能数据是过时的,若正在进行第一次同步,数据集有可以是空的。

2)若slave-serve_stale-data设置为no,从节点会对除了(INFO和SLAVEOF)以外的所有命令返回“正在与主节点同步”。

repl-ping-slave-period 10

从节点以一个预先设置好的时间间隔向服务器发送PING。这个时间间隔可以通过repl_ping_slave_period选项改变。默认值是10秒。

repl-timeout 60

接下来的选项为以下内容设置备份的超时时间:

1)从从节点的角度,同步期间的批量传输的I/O

2)从节点角度认为的主节点超时(数据,ping)

3)主节点角度认为的从节点超时(REPLCONF ACK pings)

确认这些值比定义的repl-ping-slave-period要大,否则每次主节点和从节点之间通信低速时都会被检测为超时。

repl-disable-tcp-nodelay no

同步之后是否禁用从节点上的TCP_NODELAY?

若你选择yes,redis会使用较少量的TCP包和带宽向从节点发送数据。但这会导致在从节点增加一点数据的延时。linux内核默认配置情况下最多40毫秒的延时。

若选择no,从节点的数据延时不会那么多,但备份需要的带宽相对较多。

默认情况下我们将潜在因素优化,但在高负载情况下或者在主从节点都跳的情况下,把它切换为yes是个好主意

repl-backlog-size 1mb

设置备份的backlog大小。backlog是一个缓冲区,当从节点断开一段时间的情况时,它替从节点接收存储数据,因此当从节点重连时,通常不需要完全备份,只需要一个部分同步就可以,即把从节#点断开时错过的一部分数据接收。Backlog越大,从节点可以断开并稍后执行部分同步的断开时间就越长。只要有一个从节点连接,就会立刻分配一个backlog

repl-backlog-ttl 3600

主节点有一段时间没有与从节点连接,对应的backlog就会自动释放。接下来这个选项用于配置释放前等待的秒数,秒数从断开的那一刻开始计算。值为表示不释放。

slave-priority 100

默认优先级是100。

从节点优先级是可以从redis的INFO命令输出中查到的一个整数。当主节点不能正常工作时,redis sentinel使用它来选择一个从节点并将它提升为主节点。

低优先级的从节点被认为更适合于提升,因此若有三个从节点优先级分别是10,100,25,sentinel会选择优先级为10的从节点,因为它的优先级最低。

然而优先级值为的从节点不能升级为主节点,因此优先级为的从节点永远不会被redis sentinel提升。

==========防止主服务器在异常(或不安全)情况下执行写命令,可配置master的min-slaves-to-write和min-slaves-max-lag 10========

min-slaves-to-write 3

min-slaves-max-lag 10

主节点可以停止接受写请求,当与它连接的从节点少于N个,滞后少于M秒。

N个从节点必须是在线状态。

延迟的秒数必须

这一选项并不保证N个备份都会接受写请求,但是会限制在指定秒数内由于从节点数量不够导致的写操作丢失的情况。

若想要至少3个从节点且延迟少于10秒,这样写:

min-slaves-to-write 3

min-slaves-max-lag 10

设置某一个为0表示禁用这一功能。

默认情况下default min-slaves-to-write设置为(禁用)而min-slaves-max-lag设置为10。

slave-announce-ip 5.5.5.5

slave-announce-port 1234

当使用Docker或其他类型的容器使用端口转发或网络地址转换时,Redis复制需要特别小心,特别是在使用Redis Sentinel或其他系统(其中扫描master INFO或ROLE命令的输出情况以便于发现 #slave地址的)

问题是ROLE 命令和 INFO 输出的复制部分在发布到 master 实例中时,将显示 slave 具有的用于连接到 master 的 IP 地址,而在使用 NAT 的环境中,和 slave 实例的逻辑地址(客户机用来 #连接 slave 的地址)相比较可能会不同。

类似地,slaves 将以 redis.conf 文件中监听的端口为序列出,在重新映射端口的情况下,该端口可能与转发的端口不同。

为了解决这两个问题,从Redis 3.2.2 开始,可以强制一个 slave 向 master 通告一对任意的 IP 和端口

Redis复制如何处理key的过期

Redis过期机制能限制key的存活时间,此功能依赖于redis实例的时间计数器,但即使使用Lua脚本更改了这些key,Redis slave也能正常复制具有过期时间的key。

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

·slave无法让key过期,只能等待master让key过期。当master让一个key过期(或由于LRU算法将之清除)时,master会合成一个DEL命令并发送到所有slave。

·但由于这是master驱动的key过期行为,所以有时候master可能无法及时提供DEL命令,会导致slave内存中仍然存在逻辑上已经过期的key,为解决此问题,slave基于自己的时间计数器,向那些读操作返回“此key不存在”的消息,直到从master接收到DEL命令(在不违反数据集一致性的前提下),如此便避免了客户端能读到逻辑过期key的问题。在实际应用中,使用slave 程序进行缩放的 HTML 碎片缓存,将避免返回已经比期望的时间更早的数据项(In practical terms, an HTML fragments cache that uses slaves to scale will avoid returning items that are already older than the desired time to live,这句话没看太懂)

·在Lua脚本执行期间,不执行任何key过期操作。当一个Lua脚本运行是,理论上master的时间计数器是被冻结的,这样脚本运行时,一个给定的key只有存在于不存在两种选择。时间被冻结后,能防止key在脚本运行期间过期,能保证相同脚本发送到slave后,master和slave二者的数据集中产生相同的效果

一旦slave被提升为master,他将独自管理key的过期时间。

命令“INFO”和“ROLE”

INFO replication #只显示与复制相关的信息

ROLE #提供 master 和 slave 的复制状态以及它们的复制偏移量,连接的slaves列表等

·Master上使用命令“ROLE”

127.0.0.1:6379> role

1) "master"#当前角色

2) (integer) 1792#当前master复制偏移量,master和slave共享此偏移量,在部分重同步中,需要先获取偏移量以便确认要重同步的部分,然后才能继续进行部分重同步

3) 1) 1) "172.25.10.12"#slave IP

2) "6379"#slave PORT

3) "1792"#最后一个确认的复制偏移量

·slave上使用命令“ROLE”

127.0.0.1:6379> ROLE

1) "slave"#当前角色

2) "172.25.10.13" #master IP

3) (integer) 6379 #master PORT

4) "connected" #复制状态,从master视角看复制状态可以是:connect(实例需要连接到他的master),connecting(slave-master连接正在进#行),sync(master和slave正在尝试同步),connected(slave在线)

5) (integer) 2954 #根据master复制偏移量,slave到目前为止接收到的数据量

·哨兵模式里使用命令“ROLE”

127.0.0.1:6379> ROLE

1) "sentinel"#当前角色

2) 1) "resque-master"

2) "html-fragments-master"

3) "stats-master"

4) "metadata-master"

重新启动和故障转移后的部分重同步

从Redis 4.0 开始,当一个实例在故障转移后被提升为master时,它仍然能够与旧master 的 slaves 进行部分重同步。为此,slave 会记住前任master的旧replication ID和复制偏移量,因此即使询问旧的replication ID,其也可以将部分复制缓冲提供给连接的slave。

但,升级的slave的新replication ID将不同,因为它构成了数据集的不同历史记录。例如,master可以返回可用,并且可以在一段时间内继续接受写入命令,因此在被提升的slave中使用相同的replication ID将违反一对复制标识和偏移对只能标识单一数据集的规则。

另外,slave在关机并重新启动后,能够在RDB文件中存储所需信息,以便与master进行重同步。这在升级的情况下很有用。当需要时,最好使用“SHUTDOWN”命令来执行slave的保存和退出操作。

  • 发表于:
  • 原文链接:http://kuaibao.qq.com/s/20180505G03RV100?refer=cp_1026
  • 腾讯「云+社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。

同媒体快讯

扫码关注云+社区

领取腾讯云代金券