Aldebaran

人生最棒的感觉,就是你做到别人说你做不到的事。

0%

Redis master-slave replication(配置Redis主从复制)

三田寺円

声明

本文参考翻译自《Redis Replication documentation》

Redis master-slave replication(主从复制)的特点

Redis的主从复制机制允许slave从master那里通过网络传输拷贝到完整的数据备份。具有以下特点:

  • 异步复制。从2.8版本开始,slave能不时地从master那里获取到数据。

  • 允许单个master配置多个slave

  • slave允许其它slave连接到自己。一个slave除了可以连接master外,它还可以连接其它的slave。形成一个树状的架构。

  • master在进行主从复制时是非阻塞的,这意味着在主从复制期间,master依然能够处理客户端的请求。

  • slave在主从复制期间也是非阻塞的,也可以接受来自客户端的请求,但是它用的是之前的旧数据。可以通过配置来决定slave是否在进行主从复制时用旧数据响应客户端的请求,如果配置为否,那么slave将会返回一个错误消息给客户端。不过当新的数据接收完全后,必须将新数据与旧数据替换,即删除旧数据,在替换数据的这个时间窗口内,slave将会拒绝客户端的请求和连接。

  • 一般使用主从复制来可以实现扩展性,例如说,可以将多个slave配置为“只读”,或者是纯粹的数据冗余备份。

  • 能够通过主从复制来避免master每次持久化时都将整个数据集持久化到硬盘中。只需把master配置为不进行save操作(把配置文件中save相关的配置项注释掉即可),然后连接上一个slave,这个slave则被配置为不时地进行save操作的。不过需要注意的是,在这个用例中,必须确保master不会自动启动。更多详情请继续往下读。

主从复制工作原理

如果你为master配置了一个slave,不管这个slave是否是第一次连接上Master,它都会发送一个SYNC命令给master请求复制数据。

master收到SYNC命令后,会在后台进行数据持久化,持久化期间,master会继续接收客户端的请求,它会把这些可能修改数据集的请求缓存在内存中。当持久化进行完毕以后,master会把这份数据集发送给slave,slave会把接收到的数据进行持久化,然后再加载到内存中。然后,master再将之前缓存在内存中的命令发送给slave。

当master与slave之间的连接由于某些原因而断开时,slave能够自动重连Master,如果master收到了多个slave并发连接请求,它只会进行一次持久化,而不是一个连接一次,然后再把这一份持久化的数据发送给多个并发连接的slave。

当master和slave断开重连后,一般都会对整份数据进行复制。但从redis2.8版本开始,支持部分复制。

主从复制过程

001

执行步骤

  1. 从数据库向主数据库发送sync命令。

  2. 主数据库接收sync命令后,执行BGSAVE命令(保存快照),创建一个RDB文件,在创建RDB文件期间的命令将保存在缓冲区中。

  3. 当主数据库执行完BGSAVE时,会向从数据库发送RDB文件,而从数据库会接收并载入该文件。

  4. 主数据库将缓冲区的所有写命令发给从服务器执行。

  5. 以上处理完之后,之后主数据库每执行一个写命令,都会将被执行的写命令发送给从数据库。

PS:在Redis2.8之前,主从断线或则重启之后再重连接,都需要做一次完整的sync操作(5步骤),即使断线期间只有几条的更新操作或则是没有操作,导致系统资源极度浪费。

PS:Redis2.8之后,会用一个psync来替换sync,不会进行完整的sync操作,只需要同步断线期间的记录。相关参数:repl-backlog-sizerepl-backlog-ttl``

Master持久化功能关闭时主从复制的安全性

当有需要使用到主从复制机制时,一般都会强烈建议把master的持久化开关打开。即使为了避免持久化带来的延迟影响,不把持久化开关打开,那么也应该把master配置为不会自动启动的。

为了更好地理解当一个不进行持久化的master如果允许自动启动所带来的危险性。可以看看下面这种失败情形:

假设我们有一个redis节点A,设置为master,并且关闭持久化功能,另外两个节点B和C是它的slave,并从A复制数据。

如果A节点崩溃了导致所有的数据都丢失了,它会有重启系统来重启进程。但是由于持久化功能被关闭了,所以即使它重启了,它的数据集是空的。
而B和C依然会通过主从复制n机制从A复制数据,所以B和C会从A那里复制到一份空的数据集,并用这份空的数据集将自己本身的非空的数据集替换掉。于是就相当于丢失了所有的数据。

即使使用一些HA工具,比如说sentinel来监控master-slaves集群,也会发生上述的情形,因为master可能崩溃后迅速恢复。速度太快而导致sentinel无法察觉到一个failure的发生。

当数据的安全很重要、持久化开关被关闭并且有主从复制发生的时候,那么应该禁止实例的自启动。

不需硬盘参与的主从复制

一般情况下,一次复制需要将内存的数据写到硬盘中,再将数据从硬盘读进内存,再发送给slave。

对于速度比较慢的硬盘,这个操作会给master带来性能上的损失。Redis2.8版本开始,实验性地加上了无硬盘复制的功能。这个功能能将数据从内存中直接发送到slave,而不用经过硬盘的存储。

PS:不过这个功能目前处于实验阶段,还未正式发布(截至2018-5-3 21:55:14)。

相关配置

最基本的配置

与主从复制相关的配置比较简单,只需要把下面一行加到slave的配置文件中:

slaveof <主数据库IP> <端口>

你只需要把ip地址和端口号改一下。当然,你也可以通过客户端发送SLAVEOF命令给slave。

PS:通过命令行进行的复制,在主从断开或则主从重启之后复制信息会丢失,即不能保证持久复制,需要再次执行slaveof。但是在配置文件里写死slaveof不会有该问题。

PS:默认情况下从库是只读的,不能进行修改,需要修改需要设置配置文件中的slave-read-only为no。在命令行里执行slaveof no one可以让一个从库变成主库。

部分数据复制有一些可调的配置参数,请参考redis.conf文件。

无硬盘复制功能可以通过repl-diskless-sync来配置,另外一个配置项repl-diskless-sync-delay用来配置当收到第一个请求时,等待多个slave一起来请求之间的间隔时间。

设置只读的slave

从redis2.6版本开始,slave支持只读模式,而且是默认的。可以通过配置项slave-read-only来进行配置,并且支持客户端使用CONFIG SET命令来动态修改配置。

只读的slave会拒绝所有的写请求,只读的slave并不是为了防范不可信的客户端,毕竟一些管理命令例如DEBUGCONFIG在只读模式下还是可以使用的。如果确实要确保安全性,那么可以在配置文件中将一些命令重新命名。

也许你会感到很奇怪,为什么能够将一个只读模式的slave恢复为可写的呢,尽管可写,但是只要slave一同步master的数据,就会丢失那些写在slave的数据。不过还是有一些合法的应用场景需要存储瞬时数据会用到这个特性。不过,之后可能会考虑废除掉这个特性。

Setting a slave to authenticate to a master

如果master通过requirepass配置项设置了密码,slave每次同步操作都需要验证密码,可以通过在slave的配置文件中添加以下配置项:

masterauth <password>

也可以通过客户端在运行时发送以下命令:

config set masterauth <password>

配置至少N个slave才允许向master写数据

从redis2.8版本开始,master可以被配置为,只有当master当前有至少N个slave连接着的时候才接受写数据的请求。

然而,由于redis是异步复制的,所以它并不能保证slave会受到一个写请求,所以总有一个数据丢失的时间窗口存在。

这个机制的工作原理如下所示:

  • slave每秒发送ping心跳给master,询问当前复制了多少数据。

  • master会记录下它上次收到某个slave的ping心跳是什么时候。

  • 使用者可以配置一个时间,来指定ping心跳的发送不应超过的一个超时时间

如果master有至少N个slave,并且ping心跳的超时不超过M秒,那么它就会接收写请求。

也许你会认为这情形好似CAP理论中弱化版的C(consistency),因为写请求并不能保证数据的一致性,但这样做,至少数据丢失被限制在了限定的时间内。即M秒。

如果N和M的条件都无法达到,那么master会回复一个错误信息。写请求也不会被处理。

有两个配置项用来配置上文中提到的N和M:

min-slaves-to-write <number of slaves>
min-slaves-max-lag <number of seconds>

如果需要了解更多,请查阅redis.conf配置文件。

总结:相关的参数,注释掉的参数都是使用默认值。

################################# REPLICATION #################################
##复制选项,slave复制对应的master。
# slaveof <masterip> <masterport>

##如果master设置了requirepass,那么slave要连上master,需要有master的密码才行。
##masterauth就是用来配置master的密码,这样可以在连上master后进行认证。
# masterauth <master-password>

##当从库同主机失去连接或者复制正在进行,从机库有两种运行方式:
##     1) 如果slave-serve-stale-data设置为yes(默认设置),从库会继续响应客户端的请求。
##     2) 如果slave-serve-stale-data设置为no,除去INFO和SLAVOF命令之外的任何请求都会返回一个错误”SYNC with master in progress”。
slave-serve-stale-data yes

#作为从服务器,默认情况下是只读的(yes),可以修改成NO,用于写(不建议)。
slave-read-only yes

##是否使用socket方式复制数据。
##目前redis复制提供两种方式,disk和socket。如果新的slave连上来或者重连的slave无法部分同步,就会执行全量同步,master会生成rdb文件。
##有2种方式:disk方式是master创建一个新的进程把rdb文件保存到磁盘,再把磁盘上的rdb文件传递给slave。socket是master创建一个新的进程,直接把rdb文件以socket的方式发给slave。disk方式的时候,当一个rdb保存的过程中,多个slave都能共享这个rdb文件。socket的方式就的一个个slave顺序复制。在磁盘速度缓慢,网速快的情况下推荐用socket方式。
repl-diskless-sync no

##diskless复制的延迟时间,防止设置为0。
##一旦复制开始,节点不会再接收新slave的复制请求直到下一个rdb传输。所以最好等待一段时间,等更多的slave连上来。
repl-diskless-sync-delay 5

##slave根据指定的时间间隔向服务器发送ping请求。
##时间间隔可以通过 repl_ping_slave_period 来设置,默认10秒。
# repl-ping-slave-period 10

##复制连接超时时间。
##master和slave都有超时时间的设置。
##master检测到slave上次发送的时间超过repl-timeout,即认为slave离线,清除该slave信息。slave检测到上次和master交互的时间超过repl-timeout,则认为master离线。
##需要注意的是repl-timeout需要设置一个比repl-ping-slave-period更大的值,不然会经常检测到超时。
# repl-timeout 60

##是否禁止复制tcp链接的tcp nodelay参数,可传递yes或者no。默认是no,即使用tcp nodelay。
##如果master设置了yes来禁止tcp nodelay设置,在把数据复制给slave的时候,会减少包的数量和更小的网络带宽。但是这也可能带来数据的延迟。
##默认我们推荐更小的延迟,但是在数据量传输很大的场景下,建议选择yes。
repl-disable-tcp-nodelay no

##复制缓冲区大小,这是一个环形复制缓冲区,用来保存最新复制的命令。
##这样在slave离线的时候,不需要完全复制master的数据,如果可以执行部分同步,只需要把缓冲区的部分数据复制给slave,就能恢复正常复制状态。缓冲区的大小越大,slave离线的时间可以更长,复制缓冲区只有在有slave连接的时候才分配内存。没有slave的一段时间,内存会被释放出来,默认1m。
# repl-backlog-size 5mb

##master没有slave一段时间会释放复制缓冲区的内存,repl-backlog-ttl用来设置该时间长度。单位为秒。
# repl-backlog-ttl 3600

##当master不可用,Sentinel会根据slave的优先级选举一个master。最低的优先级的slave,当选master。而配置成0,永远不会被选举。
slave-priority 100

##redis提供了可以让master停止写入的方式,如果配置了min-slaves-to-write,健康的slave的个数小于N,mater就禁止写入。master最少得有多少个健康的slave存活才能执行写命令。
##这个配置虽然不能保证N个slave都一定能接收到master的写操作,但是能避免没有足够健康的slave的时候,master不能写入来避免数据丢失。设置为0是关闭该功能。
# min-slaves-to-write 3

#延迟小于min-slaves-max-lag秒的slave才认为是健康的slave。
# min-slaves-max-lag 10