前言

Github:https://github.com/HealerJean

博客:http://blog.healerjean.com

1、Redis复制

分布式系统为了解决单点问题,通常会将数据复制成多个副本部署到其他机器。后面的哨兵,集群都是在这个基础上的。

1.1、建立主从复制

准备工作,准备多个端口的redis,分为为6379主 6380 6381

配置方式有3种呢,常见的一种是配置文件,很简单的。第二重直接使用命令,下面就是

1.1.1、建立主从复制关系

slaveof都是从节点发起,下面这个为6379为主节点,6380为从节点
127.0.0.1:6380> slaveof 127.0.0.1 6379
OK
127.0.0.1:6380> 


开始测试 6379 主节点 添加值 

127.0.0.1:6379> set hello myson
OK
127.0.0.1:6379> get hello
"myson"
127.0.0.1:6379> 

从节点查值

127.0.0.1:6380> slaveof 127.0.0.1 6379
OK
127.0.0.1:6380> get hello
"myson"
127.0.0.1:6380> 

1.1.2、查看复制状态信息:info replication

info replication

1.1.2.1、主节点 复制状态信息

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

1.1.2.2、从节点复制状态信息

127.0.0.1:6380> info replication
# Replication
role:slave
master_host:127.0.0.1
master_port:6379
master_link_status:up
master_last_io_seconds_ago:1
master_sync_in_progress:0
slave_repl_offset:506
slave_priority:100
slave_read_only:1
connected_slaves:0
master_replid:be3b2c4a26961f6a9795346a61f1530ab3663384
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:506
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:506
127.0.0.1:6380> 

1.1.3、断开复制:slaveof no one

slaveof no one

注意:这种方式虽然断开了复制,但是从主节点之前过来的数据并没有删除,这个时候直接切换到其他节点8081作为主节点,而自己作为从节点则数据这个时候不会自动删除,需要手动删除的此时新的主节点可以使用之前没有删除的数据。这种情况不应该存在


127.0.0.1:6380> slaveof no one
OK
127.0.0.1:6380> info replication
# Replication
role:master
connected_slaves:0
master_replid:721f4f4083c2ad6d85433f30002c49a2ec0f5180
master_replid2:be3b2c4a26961f6a9795346a61f1530ab3663384
master_repl_offset:772
second_repl_offset:773
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:772
127.0.0.1:6380> keys *
1) "hello"
127.0.0.1:6380> 

1.2.2、传输延迟

主从节点一般部署在不同机器上,复制时的网络延迟就成为需要考虑的因此。

Redis的Replication有一个配置“repl-disable-tcp-nodelay”,如下


# Disable TCP_NODELAY on the slave socket after SYNC?
#
# If you select "yes" Redis will use a smaller number of TCP packets and
# less bandwidth to send data to slaves. But this can add a delay for
# the data to appear on the slave side, up to 40 milliseconds with
# Linux kernels using a default configuration.
#
# If you select "no" the delay for data to appear on the slave side will
# be reduced but more bandwidth will be used for replication.
#
# By default we optimize for low latency, but in very high traffic conditions
# or when the master and slaves are many hops away, turning this to "yes" may
# be a good idea.
repl-disable-tcp-nodelay no

yes,主节点会合并较小的TCP数据包从而节省宽带,默认发送时间间隔取决于Linux内核,一般默认40毫秒,这种配置节省了带宽。但是增大了主从直接的延迟,造成masterslave数据不一致 。适用于网络环境复杂的场景,如跨机房部署,

no,则redis master会立即发送同步数据,没有延迟,适用于网络良好,如同机器或者同机房

1.2、三种复制拓扑结构

1.2.1、一对一从

当应用写命令并发量高且需要持久化时,当主节点出现死机时候,从节点提供故障转移

为了提高性能,,当应用写命令并发量较高且需要持久化时,可以只在从节点上开启AOF。 当主节点关闭持久化功能的时候,如果主节点脱机要避免自动重启操作,因为主节点没有开启AOF自动重启后数据为空,这个时候从节点继续复制主节点会导致从节点数据也被情况。

安全的做法: 在从节点上执行slaveof no one 断开与主节点的复制关系,,再重启主节点 。从而避免这一问题。

1.2.2、一对多从

可利用多个从节点实现读写分离,对于读占比较大的场景,可以把读命令发送到从节点来分担主节点压力

同时在日常开发中如果需要执行一些比较耗时的读命令,如:keys、sort等,可以在其中一台从节点上执行。防止慢查询对主节点造成阻塞从而影响线上服务的稳定性;

缺点:对于并发量高的场景,从节点过多,主节点写命令的对从节点发送多,过度消耗网络宽带,影响主节点的负载稳定性

1.2.3、树状主从结构

当主节点需要挂载多个从节点时为了避免对主节点的性能干扰,可以采用树状主从结构降低主节点压力

从节点不但可以复制主节点数据,同时可以作为其他从节点的主节点继续向下层复制,通过引入复制中间层,可以有效降低主节点负载和需要传送给从节点的数据量,,数据实现了一层一层的向下复制

缺点:比较复杂

1.3、复制过程

1、保存主节点信息:执行完slaveof命令后从节点只保存主节点的信息地址便返回,此时建立复制流程还未开始

2、主从建立socket连接:从节点内部通过每秒运行定时任务维护复制相关逻辑,当定时任务发现存在新的主节点后,会尝试与该节点建立网络连接,若无法建立连接,定时任务会无限重试直到连接成功或执行slaveof no one命令为止

3、发送ping命令:用于检测主从之间网络套接字是否可用和主节点当前是否可接受处理命令,若从节点没收到主节点的pong回复或超时,则断开复制连接,下次定时任务会发起重连

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

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

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

1.4、数据同步

psync命令完成主从数据同步, 包括全量复制和部分复制;

全量复制:一般用于初次复制场景,当数据量大的时候,会造成很大开销,是第一次经历复制时候必须经历的阶段,

部分复制:用于处理主从复制中因为网络中断等原因操作数据丢失场景, 从节点会想主节点要求补发丢失的命令数据,如果主节点的复制解压缓冲区内存在这部分数据则直接发给从节点。这样就能保证主从复制的一致性,补发的这部分数据一般远远小于全量数据,所以开销很少

1.4.1、复制偏移量

参与复制的主节点都会维护自身的复制偏移量,主节点在处理完命令后,会把命令的长度做累加记录

作用:通过判断主从节点的复制偏移量和判断主从节点数据是否一致

统计信息在info replication 中的master_repl_offset

master_repl_offset:5374
127.0.0.1:6381> info replication
# Replication
role:master
connected_slaves:1
slave0:ip=127.0.0.1,port=6380,state=online,offset=5374,lag=1
master_replid:8f18a9158618f28da1118660be48260359a4c431
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:5374
second_repl_offset:-1
repl_backlog_active:1 //开启复制缓冲区
repl_backlog_size:1048576 //缓冲区最大长度
repl_backlog_first_byte_offset:1 //起始偏移量,计算当前缓冲区可用范围
repl_backlog_histlen:5374 //已保存数据的有效长度
127.0.0.1:6381> 


从节点每秒上报自身的复制偏移量给主节点,因此主节点也会保存从节点复制偏移量。

master_repl_offset:5486
127.0.0.1:6380> info replication
# Replication
role:slave
master_host:127.0.0.1
master_port:6381
master_link_status:up
master_last_io_seconds_ago:9
master_sync_in_progress:0
slave_repl_offset:5486
slave_priority:100
slave_read_only:1
connected_slaves:0
master_replid:8f18a9158618f28da1118660be48260359a4c431
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:5486
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:5486
127.0.0.1:6380> 

从节点在接收到主节点发送的命令后,也会累计增加自身的偏移量,统计信息在slave_repl_offset

slave_repl_offset:5486

1.4.2、复制积压缓冲区

复制积压缓冲区是保存主节点上一个固定长度的队列,默认大学为1MB,当主节点有连接的从节点slave时被创建,这时主节点master响应写命令时,不但会将命令发送给从节点,还会复制积压缓冲区

缓冲区本质上是先进先出的队列,所以能够实现最近已复制数据的功能,用于部分复制和复制数据丢失的补救,复制缓冲区统计信息保存在主节点的info replication中

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

1.4.3、主节点运行ID

每个Redis节点启动后都会动态分配一个40位的十六进制,运行id主要功能是唯一识别Redis节点,比如从节点保存主节点运行id识别自己正在复制的是哪个主节点。通过info server命令查看运行id。

当redis关闭再重启之后,运行Id会随着改变,当需要调优一些内存相关配置的时候,需要重新加载才能优化已经存在的数据,这个时候,可以使用debug reload(谨慎使用save)命令重新加载RDB,并保持运行Id不变。

run_id:2b9f085b8abd25c593d40cae11237c11dca82d67
127.0.0.1:6381> info server
# Server
redis_version:4.0.8
redis_git_sha1:00000000
redis_git_dirty:0
redis_build_id:ad83a037ffc67071
redis_mode:standalone
os:Darwin 16.7.0 x86_64
arch_bits:64
multiplexing_api:kqueue
atomicvar_api:atomic-builtin
gcc_version:4.2.1
process_id:8384
run_id:2b9f085b8abd25c593d40cae11237c11dca82d67
tcp_port:6381
uptime_in_seconds:6433
uptime_in_days:0
hz:10
lru_clock:14008501
executable:/usr/local/redis-4.0.9_3/src/redis-server
config_file:/usr/local/redis-4.0.9_3/redis.conf
127.0.0.1:6381> 

7.2、psync

psync {runId} {offset}

1、流程说明:

1、从节点发送psync命令给主节点,参数runId为主节点的运行iD,如果没有则默认值为?。参数offset为当前从节点的复制偏移量,如果是第一次参与复制则默认值为-1
2、主节点根据psync参数和自身实际情况来决定响应结果

8、心跳和异步复制

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


异步复制 主节点不但负责数据读写,还负责,把写命令同步给从节点,写命令的发送过程是异步的。也就是说执行自己的命令就OK了。

9、开发和运维总的问题

9.1、规避全量复制

1、第一次建立复制

从节点没有任何主节点数据,因此必须进行全量复制才能完成数据同步,对于这种情况全量复制无法避免,当数据量较大且流量高的时候,添加从节点时,建议在低峰时候进行操作。或者尽量规避使用大数据量的Redis节点。

节点运行id不匹配

当主从关系建立后,节点会保存主节点运行ID,如果此时主节点因故障重启,那么他的运行Id就会改变,当从节点发现主节点运行Id不匹配的时候,就会以为这是一个新的主节点,就会发生全量复制。这种情况是一定需要解决的,当主节点发送故障,手动提升从节点为主节点或者采用支持自动故障转移的哨兵或集群方案

复制挤压缓冲区不足

当网络中断的时间过长,请求的偏移量不在主节点的积压缓冲区内(队列,有大小,先进先出),则服务提供从节点数据,因此这部分会退化为全量复制。 针对这种情况,需要根据网络中断长短,粉刺数数据量增大积压缓冲区避免,避免造成全量复制

ContactAuthor