在分布式系统中,为了解决单点问题,通常会把数据复制多个副本部署到其他节点,以便满足故障恢复和负载均衡等需求。redis也是如此,它为我们提供了复制功能,实现了相同数据的多个副本。复制功能是redis高可用的基础,不管是哪种集群方案,都是基于底层的主从复制原理进行的。 配置redis主从复制 在redis的主从复制中,和其他服务一样,都有master和slave两个角色,默认每个redis节点都是主节点,每个从节点也只能有一个主节点,而主节点可以配置多个从节点。
配置主从复制有以下三种方案:
上述几种方案比较简单,这里以第二种方案进行举例即可(我这里环境为一个节点使用不同端口配置了多个实例):
[root@redis ~]# redis-cli -h 192.168.171.151 -p 6379 # 6379端口为master
192.168.171.151:6379> keys * # 现在键的数量
1) "test2"
2) "test1"
#现在启动一个6380端口的redis,指定端口6379的redis为master
[root@redis local]# redis-server /usr/local/redis2/conf/redis.conf --slaveof 192.168.171.151 6379
[root@redis local]# redis-cli -p 6380 # 登录到该slave节点
127.0.0.1:6380> keys * # 已经同步了6379端口的数据
1) "test1"
2) "test2"
# 6379端口实例新增一个键
192.168.171.151:6379> set test3 cc
OK
# 6380端口自动同步,说明主从复制无误
127.0.0.1:6380> keys *
1) "test3"
2) "test1"
3) "test2"
# 通过info replication可以查看到master节点的相关信息
127.0.0.1:6380> info replication
# Replication
role:slave # 角色为slave
master_host:192.168.171.151 # master节点的IP
master_port:6379 # master节点监听的端口
master_link_status:up # 和master的连接状态,up为正常状态,如果为down,则表示有误
master_last_io_seconds_ago:2 # 最后一次和master的io交互在2秒中前
master_sync_in_progress:0
slave_repl_offset:265 # 复制偏移量,这个值一直在变,master和slave的这个值可能会不一样,但不会相差太多
slave_priority:100
slave_read_only:1 # slave是否为只读,1表示只读,0表示可读写,可将配置文件中“replica-read-only yes”的值修改为no,表示可读写,但不建议这么做
connected_slaves:0 # 本实例连接的slave数量为0
master_replid:239992eaf88adbaf74223898ad375db1d55706ac
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:265
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:265
断开主从复制
127.0.0.1:6380> slaveof no one # 只需在slave节点执行此命令,即可断开主从复制
当断开了主从复制关系,slave节点上发生了什么?
切主操作 所谓切主操作,无非就是直接重新指向另一台redis节点作为新的master。如下:
#为方便验证,再次添加一个master
[root@redis local]# redis-server /usr/local/redis2/conf/redis.conf --slaveof 192.168.171.151 6381
127.0.0.1:6380> info replication # 查看当前master
# Replication
role:slave
master_host:192.168.171.151
master_port:6381
master_link_status:up
master_last_io_seconds_ago:4
master_sync_in_progress:0
slave_repl_offset:0
........................................
127.0.0.1:6380> keys * # 查看当前slave的数据
1) "bb"
2) "aa"
3) "cc"
127.0.0.1:6380> slaveof 192.168.171.151 6379 # 切换6379端口的实例为新主
OK
127.0.0.1:6380> keys * # 再次查看当前数据,发现已经同步为新主的数据,而旧主的数据已经被删除了
1) "test2"
2) "test3"
3) "test1"
当执行切主指令后,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,默认关闭,说明如下:
redis主从复制拓扑 Redis的复制拓扑结构可以支持单层或多层复制关系,根据拓扑复杂性可以分为以下三种:一主一从、一主多从、树状主从结构,下面来聊聊不同拓扑的区别。 1、一主一从结构:一主一从结构是最简单的复制拓扑结构,用于主节点出现宕机时从节点提供故障转移支持。当应用写命令并发量较高且需要持久化时,可以只在从节点上开启AOF,这样既保证数据安全性同时也避免了持久化,对主节点的性能干扰。但需要注意的是,当主节点关闭持久化功能时,如果主节点脱机要避免自动重启操作。因为主节点之前没有开启持久化功能自动重启后数据集为空,这时从节点如果继续复制主节点会导致从节点数据也被清空的情况,丧失了持久化的意义。安全的做法是在从节点上执行slavof no one断开与主节点的复制关系,再重启主节点从而避免这一问题。 2、一主多从结构:一主多从结构(又称为星形拓扑结构)使得应用端可以利用多个从节点实现读写分离,对于读占比较大的场景,可以把读命令发送到从节点来分担主节点压力。同时在日常开发中如果需要执行一些比较耗时的读命令,如: keys, sort等,可以在其中一台从节点上执行,防止慢查询对主节点造成阻寒从而影响线上服务的稳定性。对于写并发量较,高的场景,多个从节点会导致主节点写命令的多次发送从而过度消耗网络带宽,同时也增加了主节点的负载影响服务稳定性。 3、树状主从结构:树状主从结构使得slave node不但可以复制master node的数据,同时可以作为其他slave node的主节点继续向下层复制。通过引入复制中间层,可以有效降低主节点负载和需要传送给从节点的数据量。如:数据写入节点A后会同步到B和C节点,B节点再把数据同步到D和E节点,数据实现了一层一层的向下复制。当master node需要挂载多个slave节点时为了避免对主节点的性能干扰,可以采用树状主从结构降低主节点压力。 主从复制过程的原理 主从复制过程如下:
1、从节点执行 slaveof 命令 2、从节点只是保存了 slaveof 命令中主节点的信息,并没有立即发起复制 3、从节点内部的定时任务发现有主节点的信息,开始使用 socket 连接主节点 4、连接建立成功后,发送 ping 命令,希望得到 pong 命令响应,否则会进行重连 5、如果主节点设置了权限,那么就需要进行权限验证;如果验证失败,复制终止。 6、权限验证通过后,进行数据同步,这是耗时最长的操作,主节点将把所有的数据全部发送给从节点。 7、当主节点把当前的数据同步给从节点后,便完成了复制的建立流程。接下来,主节点就会持续的把写命令发送给从节点,保证主从数据一致性。 数据同步 Redis在2.8及以上版本使用psync命令完成主从数据同步。现在应该不会有公司用2.8以下的版本了吧?
同步过程分为:全量复制和部分复制。
复制偏移量
参与复制的master节点都会维护自身复制偏移量,主节点在处理完写入命令后,会把命令的字节长度做累计记录,统计在info replication命令下的参数master_replication,slave节点每秒上报自身复制的偏移量给主节点,主节点也会保存从节点的复制偏移量。如下:
192.168.171.151:6379> info replication
# Replication
role:master
connected_slaves:1
slave0:ip=192.168.171.151,port=6380,state=online,offset=1217,lag=1
master_replid:239992eaf88adbaf74223898ad375db1d55706ac
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:1217
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:1217
复制积压缓冲区 复制积压缓冲区时保存在主节点上的一个固定长度的队列,默认大小为1MB,复制积压缓冲区的作用为:当master向slave节点同步数据的过程中,此时,master还有可能有新数据继续写入,这些新写入的数据master并不会立即发送给slave节点,而是先写入复制积压缓冲区,待下次同步数据时,遵循先进先出的原则再同步到slave。 复制缓冲区相关配置如下:
repl-backlog-size 1mb # 指定积压缓冲区的大小
info replication指令查看到的相关信息如下:
127.0.0.1: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.171.151:6379> info server
# Server
redis_version:5.0.5
redis_git_sha1:00000000
redis_git_dirty:0
redis_build_id:8a99526af4b4558d
redis_mode:standalone
os:Linux 3.10.0-1127.13.1.el7.x86_64 x86_64
arch_bits:64
multiplexing_api:epoll
atomicvar_api:atomic-builtin
gcc_version:4.8.5
process_id:2200
run_id:5a3b10e97a982cbf4efca136e052944d8bf44bdf # 这就是运行ID
tcp_port:6379
uptime_in_seconds:5849
uptime_in_days:0
hz:10
configured_hz:10
lru_clock:1911858
executable:/root/redis-server
config_file:/usr/local/redis/conf/redis.conf
psync命令怎么用
psync:slave使用psync命令完成部分复制和全量复制的功能。语法如下:
Psync [runid] [offset] runid:从节点所复制主节点运行id,默认值为“?” offset:当前从节点已复制的数据偏移量,默认为-1。
这个了解即可,一般无需我们手动执行此命令。
全量复制的工作流程
client-output-buffer-limit replica 256mb 64mb 60
代表60秒内写的64MB或直接超过256M直接断开主和客户端,全量备份失败。部分复制工作流程 部分复制主要是Redis针对全量复制的过高开销做出的一种优化措施,使用psync (runid) (offset)命令实现。当slave正在复制master时,如果出现网络闪断或者命令丢失等异常情况时,slave会向master要求补发丢失的数据,如果master的复制积压缓冲区内存在这部分数据则直接发送给从节点,这样就可以保持主从节点复制的一致性。补发的这部分数据一般远远小于全量数据,所以开销很小。 部分复制的流程如下:
主从之间的心跳检测
主从节点在建立复制后,它们之间维护着长连接并彼此发送心跳命令。 主从心跳判断机制: 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作用:
192.168.171.151:6379> client list
id=5 addr=192.168.171.151:60686 fd=7 name= age=2482 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
id=7 addr=192.168.171.151:35290 fd=8 name= age=783 idle=0 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
异步复制 主节点不但负责数据读写,还负责把写命令同步给从节点。写命令的发送过程是异步完成,也就是说主节点自身处理完写命令后直接返回给客户端,并不等待从节点复制完成。
主节点复制流程:
由于主从复制过程是异步的,就会造成从节点的数据相对主节点存在延迟。具体延迟多少字节,我们可以在主节点执行info replication命令查看相关指标获得。如下:
192.168.171.151:6379> info replication
# Replication
role:master
connected_slaves:1
slave0:ip=192.168.171.151,port=6380,state=online,offset=1707,lag=1
master_repl_offset:1707
在上面可以看到slave0的信息,分别记录了从节点的ip和port,以及是否online(在线),offset表示当前从节点的复制偏移量,master_repl_offset
表示当前主节点的复制偏移量,两者之间的差值就是当前从节点复制延迟量。redis的复制速度取决于主从之间的网络环境。
Redis使用注意事项