Redis主从复制

在分布式系统中,为了解决单点问题,通常会把数据复制多个副本部署到其他节点,以便满足故障恢复和负载均衡等需求。redis也是如此,它为我们提供了复制功能,实现了相同数据的多个副本。复制功能是redis高可用的基础,不管是哪种集群方案,都是基于底层的主从复制原理进行的。

一、配置redis主从复制

在redis的主从复制中,和其他服务一样,都有master和slave两个角色,默认每个redis节点都是主节点,每个从节点也只能有一个主节点,而主节点可以配置多个从节点。

配置主从复制有以下三种方案:

  • 在配置文件中添加配置项:slaveof (masterHost) (masterPort)。例如:slaveof 192.168.20.2 6379。该方案需要重启redis从节点才可生效,不建议使用;
  • 在redis-server启动命令后加入“–slaveof (masterHost) (masterPort)”即可;
  • 直接登录到redis中,执行指令:slaveof (masterHost) (masterPort)即可;

上述几种方案比较简单,这里以第二种方案进行举例即可(我这里环境为一个节点使用不同端口配置了多个实例):

#启动两个redis实例,仅仅监听的端口不一样
[root@redis redis]# redis-server /opt/redis/redis_6379.conf 
[root@redis redis]# redis-server /opt/redis/redis_6380.conf  --slaveof 192.168.1.5 6379
#注意slave实例的启动方式
[root@redis opt]# redis-server /opt/redis/redis_6381.conf 
#创建几个副本实例
[root@redis opt]# redis-cli -h 192.168.1.5 -p 6379   #6379端口为master
192.168.1.5:6379> keys *
1) "a"
#现在启动一个6380端口的redis,指定端口6379的redis为master
[root@redis redis]# redis-cli -h 192.168.1.5 -p 6380   # # 6380端口为slave
192.168.1.5:6380> keys *     #确实已经同步6379实例中的数据
1) "a"
[root@redis redis]# redis-cli -h 192.168.1.5 -p 6379
192.168.1.5:6379> set test redis
#master实例添加数据
[root@redis redis]# redis-cli -h 192.168.1.5 -p 6380
192.168.1.5:6380> keys *
1) "a"
2) "test"
#从节点获取数据(确实已经同步主节点中的数据)
192.168.1.5:6380> info replication    #查看slave节点的详细信息
# Replication
role:slave    #角色为slave
master_host:192.168.1.5    #master节点的IP
master_port:6379         #master节点的监听地址
master_link_status:up     #和master的连接状态,up表示正常,down则表示不正常 
master_last_io_seconds_ago:1  #最后一次和master节点进行交互是在1秒前
master_sync_in_progress:0
slave_repl_offset:308   #复制偏移量,这个值一直在变,master和slave的这个值可能会不一样,但不会相差太多
slave_priority:100
slave_read_only:1     #slave是否为只读,1表示只读,0表示可读写,可将配置文件中“replica-read-only yes”的值修改为no,表示可读写,但不建议这么做
connected_slaves:0    #本实例连接的slave数量为0
master_replid:1a358bd61e0fc5fa4ad28da5ae4393a0b75aaa08
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:308
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:308

二、断开主从复制

192.168.1.5:6380> slaveof no one
#只需在slave节点执行此命令,即可断开主从复制

当断开主从复制关系,slave节点上发生了什么?

  • 断开与主节点复制关系;
  • slave晋升为master;
  • slave断开复制关系后,并不会抛弃原有的数据,只是无法获取主节点上的数据变化;

三、切主操作

所谓切主操作,无非就是直接重新指向另一台redis节点作为新的master。如下:

[root@redis opt]# redis-cli -h 192.168.1.5 -p 6380   #登录6380实例进行查看
192.168.1.5:6380> slaveof no one
OK
192.168.1.5:6380> slaveof 192.168.1.5 6381  #切换6381实例为master
OK
192.168.1.5:6380> info replication   #查看6380实例的详细信息
# Replication
role:slave
master_host:192.168.1.5
master_port:6381    #当前master为6381端口的redis实例
master_link_status:up
master_last_io_seconds_ago:7
         ............... # 省略部分内容
192.168.1.5:6380> keys *   #可以看出已经同步了6381实例中的数据
1) "lc"
2) "l"
3) "n"
192.168.1.5:6380> slaveof  no one
OK
192.168.1.5:6380> slaveof 192.168.1.5 6379   #再次切换6379位master
OK
192.168.1.5:6380> keys *   #可以看出已经同步了6379实例中的数据,并且将同步6381实例中的数据进行了删除
1) "a"
2) "test"

当执行切主指令后,slave发生了什么?

  • 断开与旧主的复制关系;
  • 与新节点建立主从关系;
  • 删除slave节点当前的所有数据;
  • 对新节点进行复制操作;

四、主从复制的安全性

对于数据比较重要的节点,主节点会通过设置requirepass参数进行设置密码,这时,所有的client访问都需要使用auth进行认证,因此,在配置主从时,需要配置slave节点的masterauth参数与master的requirepass参数一致,这样从节点才可以正确的连接到主节点并发起复制流程。

五、slave设置只读

默认情况下,slave使用slave-read-only=yes配置为只读模式。由于复制只能从master到slave,对于slave的任何修改master都无法感知,修改slave节点数据会造成主从数据不一致。因此建议线上不要修改从节点的只读模式。

六、传输延迟

主从节点一般部署在不同机器上,主从复制时的网络延迟就成为需要考虑的问题,Redis为我们提供了repl-disable-tcpnodelay参数用于控制是否关闭TCP_NODELAY,默认关闭,说明如下:

  • 当关闭时,主节点产生的命令数据无论大小都会及时地发送给从节点,这样主从之间延迟会变小,但增加了网络带宽的消耗。适用于主从之间的网络环境良好的场景,如同机架或同机房部署;
  • 当开启时、主节点会合并较小的TCP数据包从而节省带宽。默认发送时间间隔取决于Linux的内核,一般默认为40毫秒。这种配置节省了带宽但增大主从之间的延迟。适用于主从网络环境复杂或带宽紧张的场景,如跨机房部署;

七、redis主从复制拓补

Redis的复制拓扑结构可以支持单层或多层复制关系,根据拓扑复杂性可以分为以下三种:一主一从、一主多从、树状主从结构,下面来聊聊不同拓扑的区别。

  • 一主一从结构:一主一从结构是最简单的复制拓扑结构,用于主节点出现宕机时从节点提供故障转移支持。当应用写命令并发量较高且需要持久化时,可以只在从节点上开启AOF,这样既保证数据安全性同时也避免了持久化,对主节点的性能干扰。但需要注意的是,当主节点关闭持久化功能时,如果主节点脱机要避免自动重启操作。因为主节点之前没有开启持久化功能自动重启后数据集为空,这时从节点如果继续复制主节点会导致从节点数据也被清空的情况,丧失了持久化的意义。安全的做法是在从节点上执行slavof no one断开与主节点的复制关系,再重启主节点从而避免这一问题;
  • 一主多从结构:一主多从结构(又称为星形拓扑结构)使得应用端可以利用多个从节点实现读写分离,对于读占比较大的场景,可以把读命令发送到从节点来分担主节点压力。同时在日常开发中如果需要执行一些比较耗时的读命令,如: keys, sort等,可以在其中一台从节点上执行,防止慢查询对主节点造成阻寒从而影响线上服务的稳定性。对于写并发量较高的场景,多个从节点会导致主节点写命令的多次发送从而过度消耗网络带宽,同时也增加了主节点的负载影响服务稳定性;
  • 树状主从结构:树状主从结构使得slave node不但可以复制master node的数据,同时可以作为其他slave node的主节点继续向下层复制。通过引入复制中间层,可以有效降低主节点负载和需要传送给从节点的数据量。如:数据写入节点A后会同步到B和C节点,B节点再把数据同步到D和E节点,数据实现了一层一层的向下复制。当master node需要挂载多个slave节点时为了避免对主节点的性能干扰,可以采用树状主从结构降低主节点压力;

八、主从复制原理

主从复制流程图:
20200406184833

流程:

  • 1)从节点执行slaveof指令;
  • 2)从节点只是保存了slaveof命令中主节点的信息,并没有立即发起复制;
  • 3)从节点内部的定时任务发现有主节点的信息,开始使用socket连接主节点;
  • 4)连接成功后,发送ping命令,希望得到pong命令响应,否则会进行重连;
  • 5)如果主节点设置了权限,那么就需要进行权限验证;如果验证失败,复制终止;
  • 6)权限验证通过后,进行数据同步,这是耗时最长的操作,主节点将把所有的数据全部发送给从节点;
  • 7)当主节点把当前的数据同步给从节点后,便完成了复制的建立流程。接下来,这节点就会持续的把写命令发送给从节点,保证主从数据一致性;

九、数据同步

Redis在2.8及以上版本使用psync命令完成主从数据同步。现在应该不会有公司用2.8以下的版本了吧?

同步过程分为:

  • 全量复制:一般用于初次复制场景,Redis早期支持的复制功能只有全量复制,它会把主节点全部数据一次性发送给从节点。当数据量较大时,会对主从节点和网络造成很大的开销;
  • 部分复制:用于处理在主从复制中因网络闪断等原因造成的数据丢失场景,当从节点,再次连上主节点后,如果条件允许,主节点会补发丢失数据给从节点。因为补发的数据远远小于全量数据,可以有效避免全量复制的过高开销;

十、复制偏移量

参与复制的master节点都会维护自身复制偏移量,主节点在处理完写入命令后,会把命令的字节长度做累计记录,统计在info replication命令下的参数master_replication,slave节点每秒上报自身复制的偏移量给主节点,主节点也会保存从节点的复制偏移量。如下:

192.168.1.5:6379> info replication
# Replication
role:master
connected_slaves:1
slave0:ip=192.168.1.5,port=6380,state=online,offset=8778,lag=0
master_replid:7236a4891f3026f9133b28f9f54755c3cac2b8db
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:8778
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:8778

十一、复制积压缓冲区

复制积压缓冲区是保存在主节点上的一个固定长度的队列,默认大小为1MB,复制积压缓冲区的作用为:当master向slave节点同步数据的过程中,此时,master还有可能有新数据继续写入,这些新写入的数据master并不会立即发送给slave节点,而是先写入复制积压缓冲区,待下次同步数据时,遵循先进先出的原则再同步到slave。

复制缓冲区相关配置如下:

repl-backlog-size 1mb     # 指定积压缓冲区的大小

info replication指令查看到的相关信息如下:

192.168.1.5:6380> info replication
repl_backlog_active:1    //开启复制缓冲区
repl_backlog_size:1048576   //缓冲区最大长度
repl_backlog_first_byte_offset:2626   //起始偏移量,计算当前缓冲区可用范围
repl_backlog_histlen:5138   //已保存数据的有效长度

master节点的运行ID

每个redis节点启动后都会分配一个动态的运行ID,每重启一次此ID都会发生变化,运行ID的作用是来表示唯一识别redis的节点,用命令info server可以查看当前节点的运行ID,如下:

192.168.1.5:6380> info server
# Server
redis_version:5.0.8
redis_git_sha1:00000000
redis_git_dirty:0
redis_build_id:fa5647e7230f17e4
redis_mode:standalone
os:Linux 3.10.0-862.el7.x86_64 x86_64
arch_bits:64
multiplexing_api:epoll
atomicvar_api:atomic-builtin
gcc_version:4.8.5
process_id:60925
run_id:735303f005fe9834c8cc1b76ccc7e09f204bd13a  #运行的ID
tcp_port:6380
uptime_in_seconds:7935
uptime_in_days:0
hz:10
configured_hz:10
lru_clock:9114159
executable:/opt/redis-server
config_file:/opt/redis/redis_6380.conf

十二、全量复制的工作流程

流程:

  • 1)从节点发送psync命令复制请求,默认值为“?”和“-1”;
  • 2)master判定为全量复制回复“FULL resync”响应;
  • 3)slave接受主节点的响应,保存master的运行ID和偏移量offset;
  • 4)master执行bgsave保存RDB持久化到本地;
  • 5)master发送RDB文件给slave节点,slave节点接受RDB文件保存到本地,并直接作为自身的数据文件;
  • 6)slave节点接受RDB快照到完成期间,master仍然在响应读写指令,因此master会将这期间写的指令保存在buffer缓冲区内,从节点加载完RDB文件后,主节点再把缓冲区内的数据发送给slave节点,保证数据一致性。特殊情况:设置client-output-buffer-limit replica 256mb 64mb 60 代表60秒内写的64MB或直接超过256M直接断开主和客户端,全量备份失败;
  • 7)从节点接收完主节点传送来的全部数据后会清空自身的旧数据;
  • 8)从节点清空数据后开始加载RDB文件;
  • 9)从节点加载完RDB后,如果当前节点会开启AOF持久化功能,就会立刻执行bgrewriteaof的操作;

十三、部分复制工作流程

部分复制主要是Redis针对全量复制的过高开销做出的一种优化措施,使用psync (runid) (offset)命令实现。当slave正在复制master时,如果出现网络闪断或者命令丢失等异常情况时,slave会向master要求补发丢失的数据,如果master的复制积压缓冲区内存在这部分数据则直接发送给从节点,这样就可以保持主从节点复制的一致性。补发的这部分数据一般远远小于全量数据,所以开销很小。

流程:

  • 1)主从节点之间断开连接时,如果超过reply-timeout时间,master会认为slave出现了故障,并且中断复制连接;
  • 2)主从连接中断期间,主节点依然响应命令,但因为复制命令无法发送给从节点,不过主节点内部存在复制积压缓冲区,依然保证最近一段时间的写入命令不丢失,默认情况下缓存1M;
  • 3)主从节点网络恢复之后,slave会再次连接上master;
  • 4)由于slave之前保存了自身已复制的偏移量和主节点的运行id,从会把他们当作psync指令发送给master,要求进行复制操作;
  • 5)master连接到psync命令后首先核对参数runid是否一致,如果一致,当前节点为主节点,主节点发送continue表示可以进行部分复制;
  • 6)master根据偏移量把复制积压缓冲区里的数据发送给slave;

十四、主从之间的心跳检测

主从节点在建立复制后,它们之间维护着长连接并彼此发送心跳命令。

主从心跳判断机制:

  • 1)主从节点彼此都有心跳检测机制,各自模拟成对方的客户端进行通信,通过client list命令查看复制相关的客户端信息,主节点的连接状态为Flags:M,从节点的连接状态为flags:S;
  • 2)主节点默认每隔10秒发送给从ping命令,判断从节点的存活性和连接状态。可以通过repl-ping-slave-pe从节点在主线程中每隔1秒发送 replconf ack {offset} 给主节点上报自身当前的复制偏移量。riod 10设置多少秒发送ping命令;
  • 3)从节点在主线程中每隔1秒发送 replconf ack {offset}给主节点上报自身当前的复制偏移量;

Replconf作用:

  • 1)实时检测主从节点网络状态;
  • 2)上报自身复制偏移量,检测复制数据是否丢失,如果从节点数据丢失,再从主节点的积压复制缓冲区里拉取丢失数据;
  • 3)实现保证从节点的数量和延迟性功能,通过min-slaves-to-write、min-slaves-max-lag参数定义 主节点根据replconf命令判断从节点超时时间,体现在info replication统计中的lag信息中,lag表示与从节点最后一次通信延迟的秒数,正常延迟应该在0和1之间。如果超过repl-timeout配置的值(默认60秒),则判定从节点下线并断开复制客户端连接。即使主节点判定从节点下线后,如果从节点重新恢复,心跳检测会继续进行;
192.168.1.5:6379> client list
id=7 addr=192.168.1.5:36707 fd=7 name= age=7525 idle=1 flags=S db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 events=r cmd=replconf
id=9 addr=192.168.1.5:54246 fd=8 name= age=6 idle=0 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=26 qbuf-free=32742 obl=0 oll=0 omem=0 events=r cmd=client

十五、异步复制

主节点不但负责数据读写,还负责把写命令同步给从节点。写命令的发送过程是异步完成,也就是说主节点自身处理完写命令后直接返回给客户端,并不等待从节点复制完成。

主节点复制流程:

  • 1)主节点的端口接收处理命令;
  • 2)命令处理完之后返回响应结果;
  • 3)对于修改命令异步发送给slave,slave在主线中执行复制命令;

由于主从复制过程是异步的,就会造成从节点的数据相对主节点存在延迟。具体延迟多少字节,我们可以在主节点执行info replication命令查看相关指标获得。如下:

192.168.1.5:6379> info replication
# Replication
role:master
connected_slaves:1
slave0:ip=192.168.1.5,port=6380,state=online,offset=11424,lag=0
master_replid:7236a4891f3026f9133b28f9f54755c3cac2b8db
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:11424
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:11424

在上面可以看到slave0的信息,分别记录了从节点的ip和port,以及是否online(在线),offset表示当前从节点的复制偏移量,master_repl_offset表示当前主节点的复制偏移量,两者之间的差值就是当前从节点复制延迟量。redis的复制速度取决于主从之间的网络环境。

十六、redis使用注意事项

  • 读写分离方面:需要注意数据同步是否有较大的延迟,可以编写外部监控程序采集主从节点的复制偏移量,当延迟较大时触发报警。如使用shell来采集这些数据,发送到zabbix来实现实时监控;
  • 删除策略:删除策略主要有两种:惰性删除和定时删除,所谓惰性删除,每次处理读命令时,都会检查键是否超时,如果超时,则执行del指令删除,然后del指令会异步发送给从节点。所谓定时删除,则表示主节点会在内部定时采样一定数量的键,当发现采样的键过期后,执行del指令,之后再同步给从节点,如果此时数据大量超时,主节点采样速度跟不上键过期的速度,且主节点没有读取过期键的操作,那么从节点将无法收到del命令,这个问题在redis 3.2已经解决;
  • 主从配置不一致:对于有些配置主从之间是可以不一致,比如:主节点关闭AOF在从节点开启。但对于内存相关的配置必须要一致,比如maxmemory,hash-max-ziplist-entries等参数。当配置的maxmemory从节点小于主节点,如果复制的数据量超过从节点maxmemory时,它会根据maxmemory-policy策略进行内存溢出控制,此时从节点数据已经丢失,但主从复制流程依然正常进行,复制偏移量也正常。修复这类问题也只能手动进行全量复制。当压缩列表相关参数不一致时,虽然主从节点存储的数据一致,但实际内存占用情况差异会比较大;
  • 规避全量复制:第一次建立连接复制:由于是第一次建立复制,从节点不包含任何主节点数据,因此必进行全量复制才能完成数据同步。对于这种情况全量复制无法避免。当对数据量较大且流量较高的主节点添加从节点时,建议在低峰时进行操作,或者尽量规避使用大数据量的Redis节点;
  • 节点运行ID不匹配:当主从复制关系建立后,从节点会保存主节点的运行ID,如果此时主节点因故障重启,那么它的运行ID会改变,从节点发现主节点运行ID不匹配时,会认为自己复制的是一个新的主节点从而进行全量复制。对于这种情况应该从架构上规避,比如提供故障转移功能。当主节点发生故障后,手动提升从节点为主节点或者采用支持自动故障转移的哨兵或集群方案;
  • 复制积压缓冲区不足:当主从节点网络中断后,从节点再次连上主节点时会发送psync (offset) (runId)命令请求部分复制,如果请求的偏移量不在主节点的积压缓冲区内,则无法提供给从节点数据,因此部分复制会退化为全量复制。针对这种情况需要根据网络中断时长,写命令数据量分析出合理的积压缓冲区大小。网络中断一般有闪断、机房割接、网络分区等情况。这时网络中断的时长一般在分钟级(netbreak time),写命令数据量可以统计高峰期主节点每秒info replication的master_repl_offset差值获取(write_size_per_minute)。积压缓冲区默认为1MB,对于大流量场景显然不够,这时需要增大积压缓冲区,保证repl-backlog_size > net_break_ time * write_size_per_minute,从而避免因复制积压缓冲区不足造成的全量复制;
  • 复制风暴:指大量从节点对同一主节点或者对同一台机器的多个主节点短时间内发起全,量复制的过程。复制风暴对发起复制的主节点或者机器造成大量开销,导致CPU、内存、带宽消耗。因此我们应该分析出复制风暴发生的场景,提前采用合理的方式规避。比如:减少slave的数量,采用链式的主从复制方案,如:B作为A的slave,C作为B的slave…..这样,每个master都只需要给一个slave发送RDB文件进行数据同步,但所有节点的数据却是一致的;
*************** 当你发现自己的才华撑不起野心时,就请安静下来学习吧!***************
原文地址:https://www.cnblogs.com/lvzhenjiang/p/14359827.html