引子
在上一篇文章《Redis学习笔记(一):Redis持久化——RDB与AOF》中,我记录了一些我学习Redis持久化——RDB与AOF的一些知识点。学习完Redis的持久化机制,下面我紧接着开始学习Redis的主从复制模式。
运行环境
CentOS 7.6 Redis 5.0.5(这里直接使用我写文章时的最新版,5.0.5)
Redis主从复制集群搭建
这里,Redis的安装过程我就不加赘述了。最终,我们安装好 一主三从 的集群,安装路径如下:
/home/tom/data/redis-5.0.5/master-1
/home/tom/data/redis-5.0.5/slave-1
/home/tom/data/redis-5.0.5/slave-2
/home/tom/data/redis-5.0.5/slave-3
安装好了后,我们需要修改一下配置文件。
master和slave都需要修改:
# 修改IP绑定
bind 0.0.0.0
# 后台运行
daemonize yes
# 需要密码访问
requirepass 12345
# 默认情况下,所有日志信息会输出到控制台;但是我们设置了 daemonize = yes,所以,推荐配置日期输出;
logfile logs/redis.log
这里,我采用的是修改IP绑定,而不是 直接备注掉 bind 配置项:【# bind 127.0.0.1】,为什么这么处理,在后文【问题解决】第3点中会详细解释;
同时,由于上面我们配置了日志输出,我们需要手动创建 logs 目录,否则Redis启动时将会报错,详见后文【问题解决】中的第1点。
master修改:
# 备注掉第一条RDB持久化策略
# save 900 1
# 修改这条策略,我们修改为 每5分钟持久化一次,主要用于 测试观察
save 300 1
修改了上面的配置后,如果在每5分钟内,只要有1条更改记录,master 就回进行一次持久化操作。
所有 slave修改:
slaveof <masterip> <masterport>旧的配置方式- replicaof <masterip> <masterport> 这是最新的配置方式
在Redis 5.0.5 版本中,该配置项已经改为上面第二个配置项了,但是坑爹的是,截至我写这篇文章的时候,官方文档上写的还是 slaveof 192.168.1.1 6379;在本文中,我们将采用最新的这种方式进行配置;
在所有Slave Redis节点的配置项中,加入下面的配置:
# 分别修改所有Slave节点的端口分别为 6381/6382/6383
port 6381
# 主从复制配置,Master节点IP 端口配置;
replicaof 10.247.62.120 6397
# Master节点 密码
masterauth 12345
# 因为我的Master Slave 都部署在同一台服务器上,所以,还需要分别修改所有pid文件的位置;格式:redis_<port>.pid
pidfile /var/run/redis_6381.pid
就这样,分别修改好所有Slave的配置。修改完毕后,先启动Master-1节点,再启动所有Slave节点。查看日志,应该没有异常信息;至此,我们Redis主从模式集群就搭建完毕了。如果启动的时候,还是报logs目录不存在,请参考【问题解决】第2点。下面是日志输出:
如图所示,上图中,从上到下,从左向右,分别是Master-1、Slave-1、Slave-2、Slave-3。我们依次启动这些Redis实例,并且查看Master-1的日志输出,我们可以看到在Master启动后,便开始 监听连接请求。在每一个Slave实例启动后,便会主动向Master节点发送同步请求。这里,具体的同步处理方式我们下面会详细讲。至此,一个1主3从的Redis主从集群就搭建完毕了。
主从复制概述
前一篇文章中,我详细介绍了Redis的两种持久化方式。当我们的配置开启了Redis持久化后,Redis会根据策略,将内存中的数据以文件的形式保存到磁盘上保存。这样,当我们的Redis服务挂掉、Redis所在主机重启等情况出现时,我们重启Redis,Redis将会自动从文件中恢复之前保存的数据。
但是,持久化仅仅是将Redis的数据保存在Redis服务所在的那一台主机上,这样,当我们主机出现硬件故障时,数据不可恢复,就会丢失。并且,这一台Redis要同时负载读写请求,在请求在请求巨多的时候,压力也不小。
为了解决上面提到的问题,我们Redis提供了一种简单的集群方式:主从复制。我们通过配置,可以使得 从Redis实例(Slave)可以成为 主Redis实例(Master)的精确拷贝。每当Slave和Master之间的连接断开时,Slave将会自动重新连接到Master请求同步,期间无论Master发生了什么数据改变,Slave都将尝试成为Master的精确副本。
在上文中,我们就搭建了如上图所示的主从集群。从前文我给出的四个节点的启动日志也可以看出,当Slave连接上Master时,会立即向Master发起同步请求,尝试成为Master的精确拷贝。
除了上图我给的这种集群方式,Salve节点还可以拥有自己的Slave节点,如下图:
上面像Slave-3、Slave-4这种Slave称为“Sub-Slave”。对于Slave-1实例来说,Slave-1在主从复制的时候,其角色既是Master-1的从节点,又是Slave-3的Master节点。这点,后面会详细讲到。
Redis主从复制机制
上面我提到了,每当Master和Slave的连接断开后重新建立连接时,Slave总是会主动请求同步,尝试成为Master的精确拷贝。其具体的同步机制如下:
- 当Master实例和Slave实例连接正常时,Master会连续发送命令流来保持对Slave的更新,以便将自身数据集的改变复制给Slave。这里数据集改变的操作包括新增、KEY过期、KEY被删除等。
- 当Master 和 Slave 之间的连接断开,由于网络问题、或者是主从意识到连接超时,Slave 会重新连接上 Master 并尝试进行部分重新同步:这意味着它会尝试只获取在连接断开期间内丢失的命令流。
- 当无法进行部分重新同步时,Slave 将请求一次完全同步。这将涉及一个更复杂的过程,在这个过程中,Master将创建一个子进程,将其所有数据写入一个RDB文件中,稍后父进程会将该文件发送到Slave,然后随着数据集的变化继续发送命令流。
我们一般根据上面三种机制,将Redis主从复制分为两个阶段:
- 同步:Slave 从本地文件,以及从Master 接收到的RDB文件 中恢复数据至内存。此时,Slave 中包含 Master中特定时间点T的所有数据。这里的特定时间点T指的是,在Master接收到 Slave 发出的 SYNC/PSYNC 命令后,执行 BGSAVE 的时间。
- 命令传播:特定时间点T后,Master持续发送新的Redis数据更新命令到Salve。
Redis默认使用的是异步复制。这里的异步体现在两方面:
- Redis复制在 Master 侧是非阻塞的。这意味着 Master 在一个或多个 Slave 进行初次同步或者部分重同步时,可以继续处理查询请求。
- Redis复制在 Slave 侧大部分也是非阻塞的。我们可以在Redis.conf中配置,使得 Slave 在进行 初始同步(initial synchronization)时,它仍然可以使用旧的数据集来处理查询请求。当然,我们也可配置,如果正在同步,那么对特定命令返回一个ERROR。这大部分操作都是非阻塞的,但是,在初始同步后,必须删除旧数据集并加载新数据集。Slave 将在这个短暂的窗口期阻止传入的连接(对于非常大的数据集,这可能长达几秒钟)。从Redis 4.0开始,我们可以配置Redis,使旧数据集的删除发生在不同的线程中,但是加载新的初始数据集仍将发生在主线程中,并且使Slave 阻塞。
对于上面第二点,所涉及到的配置项为:
replica-serve-stale-data yes
这个配置项规定了当Master 和 Slave 的连接断开,或者 主从同步在进行中 的时候,从主机对新来请求的两种处理方式:
- 默认情况下,该配置项的值被置为 yes 。此时,Slave 将会继续响应客户端的查询请求。因为同步未完成,所以,此时可能会返回过时的数据。如果这次同步是第一次同步的话,那可能还会返回空值,因为Slave 的数据集可能还是空的。
- 如果该配置项的值被置为 no,Slave 将对除INFO、replicaOF、AUTH、PING、SHUTDOWN、REPLCONF、ROLE、CONFIG、SUBSCRIBE、PSUBSCRIBE、PUNSIBE、PUBLISH、PUBSUB、COMMAND、POST、HOST:和LATENCY之外的所有类型的命令返回“SYNC with master in progress”这个ERROR。
这里,我还想补充说明一下,对上文我对标红的 初始同步(initial synchronization) 的理解。我刚开始看到这里的时候,我很疑惑:初始同步(initial synchronization) 和 初次同步(the first synchronization) 有什么区别?
读了几遍官方文档以及Redis.conf配置文件后,我的理解是:
- 初次同步(the first synchronization) 指的是,某一个Slave与Master的第一次同步。比如,新增了一个Slave节点,我们安装、配置好后,将其启动,该Slave与Master的第一次同步。也或者,我们将某一个Slave停掉,删除其所有持久化文件,然后将其启动,其也相当于一个全新的节点,将会和Master进行的一次全同步。也就是说,初次同步,指的是Slave 数据集 从无到有的一次同步。
- 初始同步(initial synchronization) 指的是,Master和Slave建立连接后,进行的第一次同步。可能是一个新的Slave的第一次同步,也可能是某一个Slave和Master断开连接,重连后的第一次同步,同步时Slave中已有的数据集并不为空。也就是说,初次同步(the first synchronization) 也可以称为一次 初始同步(initial synchronization)。
上面,描述的是我的理解,如果有不妥的地方,还请大家指出,一起进步~
如上图,左侧是Master节点,右侧是一个Slave节点。
我先启动Master节点,从文件中恢复好数据后,就会开始准备接受客户端的请求。
然后再启动Slave节点,Slave节点也会首先从文件中恢复数据。恢复完毕后,首先会尝试进行一次部分同步。上图中,由于Replication ID 匹配失败,最终还是进行了一次全同步。从Master的日志中可以看出,在Replication ID匹配失败后,Master自动执行了一次BGSAVE命令,将数据集持久化到磁盘文件中。
Redis主从复制原理
每一个Master 都有一个 replication ID 和 偏移量offset。replication ID 是一个较大的伪随机字符串,标记了一个给定的数据集。偏移量 offset,用来标识自身数据集的改变。
Master 将自己产生的复制流发送给 Slave 时,发送多少个字节的数据,自身的偏移量就会增加多少,目的是当有新的操作修改自己的数据集时,它可以以此更新 Slave 的状态。即使没有 Slave 连接到 Master ,当Master 数据发生改变时,复制偏移量也会自增,所以基本上每一对给定的 Replication ID, offset 都会准确标识一个Master的数据集版本。
当连接到 Master 时,Slave 将会使用 PSYNC 命令向 Master 发送自身数据集的 Replication ID 和已经处理了的 offset 偏移量,Master将会拿收到的 Replication ID, offset 对和自己当前的 Replication ID, offset 对比较,使得 Master 可以只发送所需的增量部分。但是,如果Master的缓冲区中没有足够的积压(backlog),或者Slave引用了一个版本太旧的历史 Replication ID,则都会发生完全同步:在这种情况下,Slave将从零开始获得数据集的完整拷贝。
上面,“Master的缓冲区中没有足够的积压”这一句我标了另一种颜色,这一句话怎么理解呢?什么是积压?
首先,我介绍一下缓存积压。参考Redis.conf中的注释以及相关的资料,我得到了以下信息:
积压(backlog)
积压(backlog)是由 Master 维护的一个全局缓冲区,有且只有一个,默认大小为1M,所有Slave 共享同一个积压空间。
上面这一段描述在Redis 5.0版本中就不适用了。在Redis5.0版本中,关于积压(backlog)的理解,正确的描述应该是:
积压(backlog)是一个缓冲区,每个Redis实例都在内存中维护有一个backlog空间,默认大小为1M。连接到同一个Master的所有Slave节点共享该Master的积压(backlog)空间。
在 命令传播 阶段,Master 不仅会发送命令到Salve,还会将命令写入积压空间;在Slave接收到命令后,不仅会更新当前Slave实例的数据,也会将命令写入当前Slave实例的积压空间。当Slave希望再次重新连接时,Slave会发送 PSYNC 命令,请求部分同步。需要注意的是,对于Sub-Slave这种模式,同步有些特殊,后面会有例子详细讲到。
backlog 的大小,我们可以通过下面的配置修改:
# repl-backlog-size 1mb
该配置项默认是被注释掉了的,默认大小为 1mb。积压空间配置得越大,能够支持的Slave断开连接的时间就越长。
只有至少连接了一个Salve时,才会分配积压。在Redis5.0版本中,这里这么理解也不对。下面是redis.conf中的注释原文:
The backlog is only allocated once there is at least a replica connected.
这里直译过来,给人的感觉就像是backlog只在Master端分配。但是实际并不是这样。我觉得这句话应该这么理解:
只有在 至少有另一台Redis实例连接到当前实例时,当前实例才会分配backlog空间。也就是说,单机Redis不会被分配backlog空间,而互连的每一台Redis实例都会分配backlog空间。
下面我给出了一个例子:
如上图,我构建出了如图所示的一个主从集群。其中,Master-1配置积压空间大小为100mb,Slave-1、Slave-2和Slave-3配置的积压空间大小为50mb、默认(1mb)、20mb。我们首先只启动Master-1,使用INFO命令查看Master-1的详情,如下图:
我解释下上图中的三个指标:
-
repl_backlog_active:用来标识当前实例的backlog是否是活动状态——当前Redis实例的backlog空间是否被分配。值为0时表示积压空间不是活动状态,没有被分配;为1时表示积压空间是活动状态,被分配。
-
repl_backlog_size:配置的积压空间的总大小——即 redis.conf 中 repl-backlog-size 指定的大小。
-
repl_backlog_histlen:当前Redis实例维护的backlong空间中的数据大小。
当我们只启动了Master-1时,我们可以看到,其 repl_backlog_size 大小为100mb,同时 repl_backlog_active 为 未分配(0) 状态。
然后我们分别启动Slave-1、Slave-2和Slave-3。启动完成后,我们再分别使用INFO命令查看四台Redis实例的详情,结果如下图:
从上图可以看出,当所有的Slave启动成功后,四台Redis实例的 repl_backlog_active 状态全部都变为1,表示backlog空间被分配。四台Redis实例的 repl_backlog_size 属性值各不相同,而且与我们配置的值对应。 虽然四台Redis实例都部署在同一台机器上,但是为他们配置的积压空间各不相同,最终查到的积压空间大小也不同。那就说明,每一台Redis实例都维护有自己的积压空间,而并不是只有Master才维护!
实际上,从Redis的源码中可以看出,在Redis4.0之前,backlog只由Master维护;从Redis4.0开始,不管是Master还是Slave,都会维护一份自己的backlog空间。
上图是Redis4.0源码的截图。图中标识的注释中作者明确写到:
Let's create the replication backlog if needed. Slaves need to accumulate the backlog regardless of the fact they have sub-slaves or not, in order to behave correctly if they are promoted to masters after a failover.
也就是说,Slave也会创建自己的backlog,并且也需要累积backlog。
下面我们看看backlog在主从同步中的作用:
如上图,左边是Master 的日志,右边是 Slave-1 的日志。Master 和 Slave-1 正常连接,两者的数据都是最新的。然后我停掉了Slave-1,从Master日志可以看出Slave-1的连接丢失。然后,我向Master中插入了一条新的数据(默认的NOTICE级别的日志不会打印数据写入积压的详细数据)。写入数据完成后,我再启动 Slave-1。我们可以看出,Slave-1请求同步时,Master直接传输了 backlog 中的数据。Master和Slave-1最终完成了一次部分同步。
而对于Sub-Slave这样的集群方式,如下图:
如上图,我配置了Slave-1是Master-1的从节点,Slave-2是slave-1的从节点。刚开始,三个实例正常连接,数据一致。然后我停掉了Slave-2,Master-1没有任何日志输出,Slave-1日志显示Slave-2断开连接。然后我向Master-1中插入了一条新的数据,再启动Slave-2。我们从日志也可以看出,对于Salve-2来说,其Master就是Slave-1。而同步时,Slave-1从其积压空间中拿出数据发送给Slave-2进行同步。最终,Slave-1和Slave-2进行一次部分同步。
现在回到上面的问题,“Master的缓冲区中没有足够的积压”是什么意思呢?
我打个比方,假设 Master和某一台Slave 断开,经过一段时间后,重新连接。在断开的时间内,总共产生了 1.5mb 的积压数据,但是Master 积压空间大小只有1mb,也就是说,积压空间中的数据,不足以覆盖段开期间产生的积压。我理解的不足,就是这个意思了。
积压自动释放
在redis.conf 中,和积压(backlog)有关的还有另外一个配置:
# repl-backlog-ttl 3600
在Master 太长时间都没有Slave连接,那么 积压空间 将会被释放。上面这个配置项,就指定了从最后一个Slave断开连接到开始释放积压缓冲区所需的秒数。在Redis5.0版本中,这个描述我觉得也不太对。上文我们说了,在集群中的每一台Redis实例都维护有一个backlog空间。redis.conf中关于此配置项的注释原文:
After a master has no longer connected replicas for some time, the backlog will be freed. The following option configures the amount of seconds that need to elapse, starting from the time the last replica disconnected, for the backlog buffer to be freed.
我觉得应该这么理解:当某一台Redis实例太长时间都没有和其他Redis实例连接时,那么积压空间就会被释放。这个配置项指定了某一台Redis实例和其他所有的Redis实例断开连接到开始释放积压缓冲区所需的秒数。
这个配置项默认也是备注了的,默认值3600秒,也就是1小时。如果该值被配置为 0,这就意味着永远不释放积压。
需要注意的,Redis 5.0.5 版本的配置文件Redis.conf 中,对该配置有下面一行注释:
Note that replicas never free the backlog for timeout, since they may be promoted to masters later, and should be able to correctly "partially resynchronize" with the replicas: hence they should always accumulate backlog.
意思就是,Slave不会因为超时而释放积压。因为它可能会在后面升级成为Master,到时候它得能够正确地和所有Slave进行部分同步。所以Slave应该总是累积 积压。这里我之前疑惑了好久,现在我的理解是:
以上图这个主从集群为例,这里我们不考虑哨兵机制。分两种情况说明:
- 当Slave-1挂掉,经过指定的时间后,Slave-2的backlog空间将会被释放,Master-1、Slave-3不受影响;
- 当Master-1挂掉,经过指定的时间后,Slave-3的backlog空间将会被释放,Slave-1、Slave-2不受影响;此时,Slave-1作为Slave-2的Master,Slave-3作为一个单机节点,他们三各自维护自己的积压空间,同时,在Slave-2短暂挂掉并恢复后,可以使用Slave-1的积压空间中的数据进行部分同步。
需要注意的是,有关backlog的一些理解,是我自己的理解,可能有的地方理解的问题。如果有朋友发现我描述有误的地方,欢迎大家指出!
主从读写分离
默认情况下,所有的Slave都是只读的。这样,Master只复杂写查询,减轻了Master的压力。同时,我们可以根据业务只对某些客户端提供只读的Salve,保证了数据的安全性。
当然,这个功能我们也是可以配置的:
# 默认情况下,Slave 都是只读
replica-read-only yes
需要注意的是,即使Slave是只读的,像 DEBUG、CONFIG 这样的管理员命令仍然可以使用。要解决这个问题,我们可以在Redis.conf配置文件中通过 rename-command 配置项来屏蔽这个问题。关于 rename-command ,这里我就不多讲了,详情请查阅Redis.conf 中的相关注释。需要注意的是,改变命令名称会被记录AOF文件中,当这被传送到Slave时可能会导致一些问题。
如果我们将Slave配置为读写的,在Redis 4.0 版本之前,如果我们通过EXPIRE命令使某个KEY过期,或者我们手动给某一个KEY设置一个过期时间,都不会起作用。读写的Slave不能通过过期时间来淘汰KEY。
在Redis 4.0之后,这个问题被解决。读写的Slave可以像Master一样使KEY过期,但是,对于DB编号大于63的KEY除外。
此外,对于读写的Slave,我们对其写的数据只会存在于当前实例,而不会同步到其从属的Slave上。Sub-Slave 总是接收与最顶层 Master 向 中间层(intermediate) Slaves 发送的复制流相同的复制流。比如这个Sub-Slave场景——
假设有这样一个主从集群:A <----- B <----- C。A是Master,B是A的Slave,C是B的Slave。B是可读可写的。
如果我们向B中写入数据,C 也不会看到 B 的写入,而是将拥有和 Master 实例 A 相同的数据集。
主从复制与持久化
当我们开启主从复制时,官方文档中强烈建议开启Master和Slave的持久化配置。如果不能开启持久化,比如磁盘性能太低、持久化太耗时 时,应该通过配置避免在主机重启后Redis实例自动重启。下面是官方文档:
In setups where Redis replication is used, it is strongly advised to have persistence turned on in the master and in the slaves. When this is not possible, for example because of latency concerns due to very slow disks, instances should be configured to avoid restarting automatically after a reboot.
下面这个例子说明了为什么要这么做。
假设Slave B、Slave C都是 Master A 的Slave节点,也就是说,B、C会从A同步数据。假设A崩溃后又重启了,由于关闭了持久化,那么重启后A的数据集就为空,B、C又会连接上A,请求同步。那么,同步的结果就是,B、C使用空集覆盖掉自己原有的数据集,B、C的数据都会被清空。
无磁盘复制
通常情况下,完全同步会将当前内存中的数据写入到一个临时的RDB文件中,然后再以流的形式将该文件发送给Slave进行数据同步。
如果磁盘性能很低的话,这对Master来说是很大的压力。从 2.8.18 版开始,Redis开始支持 无磁盘复制。开启此配置后,Redis将不会将生成的RDB文件写入到磁盘上,而是直接将文件流发送给Slave进行同步,无需使用磁盘作为中间存储介质。
下面是Redis.conf中的该配置项:
repl-diskless-sync no
默认情况下,该功能是关闭的。作者在Redis.conf特别备注了,该功能目前只是一项实验性的功能!
该策略的不同,也会导致完全同步时的工作机制不同:
- 默认情况下,当该配置项被置为 no。全量同步时,Master会创建一个子进程,将RDB文件写入磁盘。稍后,该文件将由父进程增量传输到Slave。
- 当该配置项被置为 yes,开启了无磁盘复制。全量同步时,Master也会创建一个子进程,直接将RDB文件写入Slave Socket,而不会将文件写入磁盘。
使用磁盘备份复制,在RDB文件生成的同时,所有Slave便可以开始排队等待;当子进程写入RDB文件完成时,所有的Slave可以一起开始进行同步。相反,使用无盘复制,一旦传输开始,新到达的Slave将排队,只有要当前Slave传输终止时,新的传输才能开始。
在 磁盘非常慢,或者宽带很好时,无磁盘复制表现较好。
使用无盘复制时,Master在开始传输之前会等待一段时间,等待更多的Slave能够到达,Master和这些Slave之间的数据传输能够并行进行。这个时间也是可以配置的,下面是配置项:
repl-diskless-sync-delay 5
该配置项的单位为“秒”,默认情况下,如果开启无磁盘复制,那么在传输开始前,会等待5秒。如果想禁用等待,将该配置项的值置为0即可。
其他主从复制相关的配置
# repl-ping-replica-period 10
Slave会每隔一段时间向Master发送 PING 命令,可以理解为Slave PING Master的心跳间隔。默认值是 10秒。
# repl-timeout 60
这个配置项规定了主从复制的超时时间,默认是 60秒。在下面三种情况下,认为本次复制超时:
- 在Slave在向Master发送SYNC/PSYNC命令后指定时间内,Slave没有收到Master传输的RDB快照数据包;
- 在指定时间内,Slave没有收到任何来自Master的数据包或者PING;
- 在指定时间内,Master没有收到REPCONF ACK确认信息。
需要注意的是,一定要确保该值大于上面为 repl-ping-replica-period 指定的值,否则每次Master和Slave之间通信较慢时都会检测到超时。
repl-disable-tcp-nodelay no
是否在SYNC命令执行后在 Slave 的 Socket 上禁用 TCP_NODELAY 。TCP_NODELAY这里我不详细讲了~
如果选择“yes”,Redis将使用更少的TCP数据包和更少的带宽向Slave发送数据。但是这可能会增加数据出现在Slave端的延迟,对于使用默认配置的Linux内核来说,延迟最长可达40毫秒。
如果选择“no”,数据出现在Slave端的延迟将会减少,但更多带宽将用于复制。
默认情况下,该配置项被置为“no”,针对低延迟进行优化,但是在非常高的流量条件下,或者当Master服务器和Slave服务器相距很远时,将此选项设为“yes”可能是个好主意。
还有一些一些其他的配置,这里我就不详细列出了,具体的,可以参考 Redis.conf 文件。至此,有关Redis 主从复制相关的知识点,我就总结完毕了。部分和哨兵机制有关的知识点,我会在下一篇文章中单独讲。
问题解决
1、启动报错:找不到日志目录,报错信息如下:
*** FATAL CONFIG FILE ERROR ***
Reading the configuration file, at line 171
>>> 'logfile "logs/redis.log"'
Can't open the log file: No such file or directory
这里,是因为上面我们配置了日志输出,输出到 mastet-1/logs/reids.log/、slave-1/logs/redis.log等路径下,默认情况下,master-1、slave-1等的目录下并没有 logs 目录,我们需要手动创建 logs 目录,否则Redis启动时将会报错。我们手动在每个Redis实例的目录下,创建一个 logs 目录即可。
2、每个节点下都新建了logs目录,但是启动Redis的时候,仍然报logs目录不存在。
这里,需要注意一下,目前,我们的目录结构为:
/redis-5.0.5/
/redis-5.0.5/master-1/
/redis-5.0.5/master-1/logs/
/redis-5.0.5/slave-1/
/redis-5.0.5/slave-1/logs
我的理解是,我们在 redis.conf 配置文件中,配置的日志文件路径为:logs/redis.log,是一个相对路径;而这个相对路径的起点,就是我们当前光标所处的目录;(个人理解,不确定;如果有知道的同学,欢迎赐教!)所以,如果我们在 /redis-5.0.5/ 目录下执行:
./master-1/bin/redis-server ./master-1/redis.conf
这里,实际上是在找 /redis-5.0.5/logs/ 目录是否存在;很显然是不存在的;所以,我们在启动的Redis的时候,需要进入到每个Redis节点目录下,再使用命令启动:
cd master-1
./bin/redis-server redis.conf
cd ../slave-1
./bin/redis-server redis.conf
此外,在实际生产环境中,我们一般采用 绝对路径 的方式进行配置!
3、Master与Slave节点同步失败,报错:
10005:S 19 Sep 2019 14:32:07.990 * Connecting to MASTER 10.247.62.120:6397
10005:S 19 Sep 2019 14:32:06.969 * MASTER <-> REPLICA sync started
10005:S 19 Sep 2019 14:32:07.974 # Error condition on socket for SYNC: Connection refused
很明显,Connection refused,Master与Slave交互被拒绝,肯定跟Master的网络配置有关。查看Master节点的配置,发现有下面两条配置:
# IP 绑定被直接备注掉了,而不是绑定为 0.0.0.0 或 其他IP
# bind 127.0.0.1
# protected-mode状态为 yes 开启状态
protected-mode yes
首先,简单介绍下 bind 配置项。下面是Redis.conf配置文件中的注释:
By default, if no "bind" configuration directive is specified, Redis listens for connections from all the network interfaces available on the server. It is possible to listen to just one or multiple selected interfaces using the "bind" configuration directive, followed by one or more IP addresses.
意思就是,默认情况下,如果没有 bind 配置被直接指定,Redis 就会监听网络上所有可用的连接。当然,也可以通过配置 bind 项,设置Redis只监听一个或多个IP的连接。
然后,再看看 protected-mode 配置项相关的注释:
When protected mode is on and if:
1) The server is not binding explicitly to a set of addresses using the "bind" directive.
2) No password is configured.
The server only accepts connections from clients connecting from the IPv4 and IPv6 loopback addresses 127.0.0.1 and ::1, and from Unix domain sockets.
意思就是,当 protected mode 开启,并且以下任一条件满足:
- 该Redis Server未使用 bind 配置项显示绑定到一组IP地址;
- 没有配置密码;
此时,该Redis Server仅接受来自IPv4和IPv6环回地址127.0.0.1和 ::1 以及Unix域套接字的客户端的连接。
那也就是说,咱们直接备注掉了 bind 配置项,然后又开启了 protected mode ,此时,Redis就只接受来来自 127.0.0.1 的连接了。
这个问题,解决方案有两种:
- 修改 bind 项配置:
# 0.0.0.0 即表示允许所有IP地址连接
bind 0.0.0.0
- 关闭 protected mode :
protected-mode no
这里,我采用的是 第一种方式,修改Master 节点的 bind 配置值为 0.0.0.0;修改完毕后,再重新启动Master节点,可以看到Slave-1节点的日志输出了同步成功的日志:
10283:S 19 Sep 2019 15:27:18.894 * Connecting to MASTER 10.247.62.120:6379
10283:S 19 Sep 2019 15:27:18.894 * MASTER <-> REPLICA sync started
10283:S 19 Sep 2019 15:27:18.895 * Non blocking connect for SYNC fired the event.
10283:S 19 Sep 2019 15:27:18.896 * Master replied to PING, replication can continue...
10283:S 19 Sep 2019 15:27:18.898 * Partial resynchronization not possible (no cached master)
10283:S 19 Sep 2019 15:27:18.900 * Full resync from master: 749cf049621b2e7ca9c81c02cb3b17866c7c6454:0
10283:S 19 Sep 2019 15:27:18.911 * MASTER <-> REPLICA sync: receiving 175 bytes from master
10283:S 19 Sep 2019 15:27:18.911 * MASTER <-> REPLICA sync: Flushing old data
10283:S 19 Sep 2019 15:27:18.911 * MASTER <-> REPLICA sync: Loading DB in memory
10283:S 19 Sep 2019 15:27:18.911 * MASTER <-> REPLICA sync: Finished with success
如果此时还是同步失败,请检查服务的防火墙配置。如果是在虚拟机起的Linux虚拟机,可以参考我的另一篇文章《SSH访问VMWare的Linux虚拟机》,配置相关的端口映射、防火墙设置等。
总结
这一篇文章,大概写了一周,自己写完,受益良多。这里记录下来,希望也能帮到大家。如果有什么地方描述的有问题,或者我理解错误,还请大家不吝赐教,欢迎留言交流~
参考文档
1、https://redis.io/topics/replication
2、redis.conf
3、http://www.redis.cn/topics/replication.html
4、https://www.cnblogs.com/daoluanxiaozi/p/3724299.html
5、https://juejin.im/post/5b67029c6fb9a04fa42fd592
7、http://mdba.cn/2015/03/17/redis主从复制(2)— replication buffer与replication backlog