Redis(1.9)Redis主从复制

【1】实验环境

  CentOS7.5 + Redis4.0.11

  架构:原生1主2从,做实验机器有限,从库双实例

  主库:192.168.135.170

  从库1:192.168.135.171~6379    

  从库2:192.168.135.171~6479

【2】配置文件

更多参考:Redis(1.5)Redis配置文件(4.0.14)

注意,不要直接复制,可能会导致错误,特别是 masterauth,最要用原始代码,把注释去掉即可

#主库核心参数
daemonize yes logfile
"/data/redis/log/redis.log" requirepass 123456 bind 0.0.0.0 pidfile /data/redis/conf/redis.pid dbfilename redis.rdb dir /data/redis/conf/
#默认支持持久化,可以把主库持久化取消,把save 900 1 等等之类的注释掉
save ""
#从库1~6379核心参数
daemonize yes logfile "/data/redis/log/redis.log" requirepass 123456 bind 0.0.0.0 pidfile /data/redis/conf/redis.pid dbfilename redis.rdb dir /data/redis/conf/
timeout 300
port 6379
#如果主库设置了密码,那么从库必须配置该参数
masterauth 123456

#从库2~6479核心参数
daemonize yes logfile "/data/redis/log/redis6479.log" requirepass 123456 bind 0.0.0.0 pidfile /data/redis/conf6479/redis6479.pid dbfilename redis6479.rdb dir /data/redis/conf6479/
timeout 300
port 6479
#如果主库设置了密码,那么从库必须配置该参数,参数值为主库密码
masterauth 123456

【3】启动查阅

redis-server /data/redis/conf6479/redis6479.conf 
redis-server /data/redis/conf/redis.conf         
ps -ef|grep redis

【4】配置主从与核验

从节点开启主从复制,有3种方式:
配置文件: 在从服务器的配置文件中加入:slaveof <masterip> <masterport>
启动命令: redis-server启动命令后加入 --slaveof <masterip> <masterport>
客户端命令: Redis服务器启动后,直接通过客户端执行命令:slaveof <masterip>
<masterport>,则该Redis实例成为从节点。
通过  info replication 命令可以看到复制的一些信息

#【我的实践】这里我们就用客户端登录的方式实验吧
#【4.1】登录上3个实例
 主:redis-cli -a 123456
 从1:redis-cli -a 123456
从2:redis-cli -a 123456 -p 6479
使用info:info 查看相关信息

#【4.2】开始配置主从
2个从库:slaveof 192.168.135.170 6379
使用命令查看复制状态:info replication
从库中,该行显示 master_link_status:down ,复制失败,如果是up则成功

   主库:
        
      

 

   从库:

      

 

 

最终验证,用keys *  查看key是否同步。

 

  

  

【5】主从复制的基本原理

主从复制的作用

  1. 数据冗余:主从复制实现了数据的热备份,是持久化之外的一种数据冗余方式。
  2. 故障恢复:当主节点出现问题时,可以由从节点提供服务,实现快速的故障恢复;实际上是一种服务的冗余。
  3. 负载均衡:在主从复制的基础上,配合读写分离,可以由主节点提供写服务,由从节点提供读服务(即写Redis数据时应用连接主节点,读Redis数据时应用连接从节点),分担服务器负载;尤其是在写少读多的场景下,通过多个从节点分担读负载,可以大大提高Redis服务器的并发量。
  4. 读写分离:可以用于实现读写分离,主库写、从库读,读写分离不仅可以提高服务器的负载能力,同时可根据需求的变化,改变从库的数量;
  5. 高可用基石:除了上述作用以外,主从复制还是哨兵和集群能够实施的基础,因此说主从复制是Redis高可用的基础。

主从复制启用

从节点开启主从复制,有3种方式:

  1. 配置文件: 在从服务器的配置文件中加入:slaveof <masterip> <masterport>
  2. 启动命令: redis-server启动命令后加入 --slaveof <masterip> <masterport>
  3. 客户端命令: Redis服务器启动后,直接通过客户端执行命令:slaveof <masterip>
    <masterport>,则该Redis实例成为从节点。
通过  info replication 命令可以看到复制的一些信息

主从复制原理

主从复制过程大体可以分为3个阶段:连接建立阶段(即准备阶段)、数据同步阶段、命令传播阶段。

在从节点执行 slaveof 命令后,复制过程便开始运作,下面图示大概可以看到,
从图中可以看出复制过程大致分为6个过程
  

 

 

 

主从配置之后的日志记录也可以看出这个流程

1)保存主节点(master)信息。
执行 slaveof 后 Redis 会打印如下日志:

  

 

 

 

2)从节点(slave)内部通过每秒运行的定时任务维护复制相关逻辑,当定时任务发现存在新的主节点后,会尝试与该节点建立网络连接
  

 

 

 


从节点与主节点建立网络连接

从节点会建立一个 socket 套接字,从节点建立了一个端口为51234的套接字,专门用于接受主节点发送的复制命令。从节点连接成功后打印如下日志:

  

 

 

 

如果从节点无法建立连接,定时任务会无限重试直到连接成功或者执行 slaveof no one 取消复制

关于连接失败,可以在从节点执行 info replication 查看 master_link_down_since_seconds 指标,它会记录与主节点连接失败的系统时间。从节点连接主节点失败时也会每秒打印如下日志,方便发现问题:

# Error condition on socket for SYNC: {socket_error_reason}

3)发送 ping 命令。
连接建立成功后从节点发送 ping 请求进行首次通信,ping 请求主要目的如下:
·检测主从之间网络套接字是否可用。
·检测主节点当前是否可接受处理命令。
如果发送 ping 命令后,从节点没有收到主节点的 pong 回复或者超时,比如网络超时或者主节点正在阻塞无法响应命令,从节点会断开复制连接,下次定时任务会发起重连。

 

 

从节点发送的 ping 命令成功返回,Redis 打印如下日志,并继续后续复制流程:
  

4)权限验证。如果主节点设置了 requirepass 参数,则需要密码验证,从节点必须配置 masterauth 参数保证与主节点相同的密码才能通过验证;如果验证失败复制将终止,从节点重新发起复制流程。

5)同步数据集。主从复制连接正常通信后,对于首次建立复制的场景,主节点会把持有的数据全部发送给从节点,这部分操作是耗时最长的步骤。

6)命令持续复制。当主节点把当前的数据同步给从节点后,便完成了复制的建立流程。接下来主节点会持续地把写命令发送给从节点,保证主从数据一致性。

 

原理部分转自:https://segmentfault.com/a/1190000018268350

 【6】主从复制的详细原理

4.0的复制参数

################################# 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

# 设置1或另一个设置为0禁用这个特性。
# Setting one or the other to 0 disables the feature.
# By default min-slaves-to-write is set to 0 (feature disabled) and
# min-slaves-max-lag is set to 10.

 

2.8 版本之后的 复制功能的实现

为了解决旧版复制功能在处理断线重复制情况时的低效问题,Redis从2.8版本开始,使用PSYNC命令代替SYNC命令来执行复制时的同步操作。

PSYNC命令具有完整重同步(full resynchronization)和部分重同步(partial resynchronization)两种模式:

  1. 其中完整重同步用于处理初次复制情况:完整重同步的执行步骤和SYNC命令的执行步骤基本一样,它们都是通过让主服务器创建并发送RDB文件,以及向从服务器发送保存在缓冲区里面的写命令来进行同步;
  2. 而部分重同步则用于处理断线后重复制情况:当从服务器在断线后重新连接主服务器时,如果条件允许,主服务器可以将主从服务器连接断开期间执行的写命令发送给从服务器,从服务器只要接收并执行这些写命令,就可以将数据库更新至主服务器当前所处的状态

部分重同步实现

部分重同步功能由以下三个部分构成:

  1. 主服务器的复制偏移量(replication offset)和从服务器的复制偏移量;
  2. 主服务器的复制积压缓冲区(replication backlog);
  3. 服务器的运行ID(run ID)(可以通过info server查看)

复制偏移量

执行复制的双方——主服务器和从服务器会分别维护一个复制偏移量:

  1. 主服务器每次向从服务器传播N个字节的数据时,就将自己的复制偏移量的值加上N;
  2. 从服务器每次收到主服务器传播来的N个字节的数据时,就将自己的复制偏移量的值加上N;

通过对比主从服务器的复制偏移量,程序可以很容易地知道主从服务器是否处于一致状态:

  1. 如果主从服务器处于一致状态,那么主从服务器两者的偏移量总是相同的;
  2. 相反,如果主从服务器两者的偏移量并不相同,那么说明主从服务器并未处于一致状态。

假设Slave A在短线之后立即重新连接master,并且成功,接下来slave A向master 发送PSYNC命令,master对比slave A的偏移量发现不相等,那么这时候是使用完全同步还是部分同步呢?

如果部分同步的话,master如何补偿slave A在短线期间的数据呢?这就和下面所说的复制积压缓冲区有关。

复制积压缓冲区(repl_backlog_size)

复制积压缓冲区是由主服务器维护的一个固定长度(fixed-size)先进先出(FIFO)队列,默认大小为1MB。

可以通过 info replication 查看详情;

  

 

 

和普通先进先出队列随着元素的增加和减少而动态调整长度不同,固定长度先进先出队列的长度是固定的,当入队元素的数量大于队列长度时,最先入队的元素会被弹出,而新元素会被放入队列。

当主服务器进行命令传播时,它不仅会将写命令发送给所有从服务器,还会将写命令入队到复制积压缓冲区里面,如图所示:

因此,主服务器的复制积压缓冲区里面会保存着一部分最近传播的写命令,并且复制积压缓冲区会为队列中的每个字节记录相应的复制偏移量,就像下表所示的那样。

当从服务器重新连上主服务器时,从服务器会通过PSYNC命令将自己的复制偏移量offset发送给主服务器,主服务器会根据这个复制偏移量来决定对从服务器执行何种同步操作:

  1. 如果offset偏移量之后的数据仍然存在于复制积压缓冲区里面,那么主服务器将对从服务器执行部分重同步操作;
  2. 相反,如果offset偏移量之后的数据已经不存在于复制积压缓冲区,那么主服务器将对从服务器执行完整重同步操作。

复制积压缓冲区的大小

Redis为复制积压缓冲区设置的默认大小为1MB,如果主服务器需要执行大量写命令,又或者主从服务器断线后重连接所需的时间比较长,那么这个大小也许并不合适。

如果复制积压缓冲区的大小设置得不恰当,那么PSYNC命令的复制重同步模式就不能正常发挥作用,因此,正确估算和设置复制积压缓冲区的大小非常重要。

复制积压缓冲区的最小大小可以根据公式 second*write_size_per_second 来估算:

  1. 其中second为从服务器断线后重新连接上主服务器所需的平均时间(以秒计算);
  2. 而 write_size_per_second 则是主服务器平均每秒产生的写命令数据量(协议格式的写命令的长度总和);

例如,如果主服务器平均每秒产生1 MB的写数据,而从服务器断线之后平均要3秒才能重新连接上主服务器,那么复制积压缓冲区的大小就不能低于3MB。

为了安全起见,可以将复制积压缓冲区的大小设2secondwrite_size_per_second,这样可以保证绝大部分断线情况都能用部分重同步来处理。

至于复制积压缓冲区大小的修改方法,可以参考配置文件中关于 repl-backlog-size 选项的说明。

服务器运行ID(可以通过info server查看)

除了复制偏移量和复制积压缓冲区之外,实现部分重同步还需要用到服务器运行ID(run ID):

  1. 每个Redis服务器,不论主服务器还是从服务,都会有自己的运行ID;
  2. 运行ID在服务器启动时自动生成,由40个随机的十六进制字符组成,例如53b9b28df8042fdc9ab5e3fcbbbabff1d5dce2b3;

 

当从服务器对主服务器进行初次复制时,主服务器会将自己的运行ID传送给从服务器,而从服务器则会将这个运行ID保存起来.

当从服务器断线并重新连上一个主服务器时,从服务器将向当前连接的主服务器发送之前保存的运行ID:

  1. 如果从服务器保存的运行ID和当前连接的主服务器的运行ID相同,那么说明从服务器断线之前复制的就是当前连接的这个主服务器,主服务器可以继续尝试执行部分重同步操作;
  2. 相反地,如果从服务器保存的运行ID和当前连接的主服务器的运行ID并不相同,那么说明从服务器断线之前复制的主服务器并不是当前连接的这个主服务器,主服务器将对从服务器执行完整重同步操作。

PSYNC命令的实现

PSYNC命令的调用方法有两种:

  1. 如果从服务器以前没有复制过任何主服务器,或者之前执行过SLAVEOF no one命令,那么从服务器在开始一次新的复制时将向主服务器发送PSYNC ? -1命令,主动请求主服务器进行完整重同步(因为这时不可能执行部分重同步);
  2. 相反地,如果从服务器已经复制过某个主服务器,那么从服务器在开始一次新的复制时将向主服务器发送PSYNC命令:其中runid是上一次复制的主服务器的运行ID,而offset则是从服务器当前的复制偏移量,接收到这个命令的主服务器会通过这两个参数来判断应该对从服务器执行哪种同步操作。

根据情况,接收到PSYNC命令的主服务器会向从服务器返回以下三种回复的其中一种:

  1. 如果主服务器返回+FULLRESYNC,那么表示主服务器将与从服务器执行完整重同步操作:其中runid是这个主服务器的运行ID,从服务器会将这个ID保存起来,在下一次发送PSYNC命令时使用;而offset则是主服务器当前的复制偏移量,从服务器会将这个值作为自己的初始化偏移量;
  2. 如果主服务器返回+CONTINUE,那么表示主服务器将与从服务器执行部分重同步操作,从服务器只要等着主服务器将自己缺少的那部分数据发送过来就可以了;
  3. 如果主服务器返回-ERR回复,那么表示主服务器的版本低于Redis 2.8,它识别不了PSYNC命令,从服务器将向主服务器发送SYNC命令,并与主服务器执行完整同步操作。

 

【附录2】常见故障处理

【问题1】I/O error reading bulk count from MASTER:Resource temporarily unavailable

  

解决办法:这是因为非root 用户的进程资源受限,主要是noproc的限制,可以改成如下值,也可以改成unlimits

【1】网络与端口查看
ping IP
telnet IP port

【2】修改Linux 用户限制
vi /etc/security/limits.conf #输入以下内容 redis  hard  nofile  10240 redis  soft  nofile  1024 redis  hard  nporc  8192 redis  soft  nproc  8192 #nproc 用户创建进程数 nofile 进程打开文件次数限制,更多参考《操作系统环境配置》

【问题2】数据延迟,主从是否同步

(1)由于redis 是异步复制,延迟是肯定的,如何查看延迟大小呢?

主节点:info replication 中的 master_repl_offset

从节点:info replication 中的 slave0字段的 offset指标

他们的差值,就是主从延迟的字节量。

(2)监控与处理

监控这个差值比如达到10M就报警,然后对客户端进行读写分离或读请求分发到其他从库,不过这种办法成本较高且操作比较麻烦。

 

posted @ 2019-10-28 14:42  郭大侠1  阅读(1106)  评论(0编辑  收藏  举报