作者
微信:tangy8080
电子邮箱:914661180@qq.com
更新时间:2019-08-26 20:53:04 星期一
欢迎您订阅和分享我的订阅号,订阅号内会不定期分享一些我自己学习过程中的编写的文章
如您在阅读过程中发现文章错误,可添加我的微信 tangy8080 进行反馈.感谢您的支持。
文章主题
了解Redis主从复制的过程
主从复制则侧重解决数据的多机热备,主从复制是实现负载均衡和故障恢复的基础
前置条件
[无]
正文
概述
在分布式系统中为了解决单点问题,通常会把数据复制多个副本部署到 其他机器,满足故障恢复和负载均衡等需求。Redis也是如此,它为我们提供了复制功能,实现了相同数据的多个Redis副本。复制功能是高可用Redis的基础,哨兵和集群都是在复制的基础上实现高可用的。
参与复制的Redis实例划分为主节点(master)和从节点(slave)。默认 情况下,Redis都是主节点。每个从节点只能有一个主节点,而主节点可以同时具有多个从节点。复制的数据流是单向的,只能由主节点复制到从节点
主从复制的配置
二进制方式安装下的配置
- 在配置文件中加入slaveof{masterHost}{masterPort}随Redis启动生效。
- 在redis-server启动命令后加入--slaveof{masterHost}{masterPort}生效。
- 直接使用命令:slaveof{masterHost}{masterPort}生效。
k8s Helm方式安装下的配置
https://github.com/helm/charts/tree/master/stable/redis
查看复制状态信息
- k8s-180 主节点
[root@k8s-180 ~]# kubectl exec -it redis-master-0 redis-cli 127.0.0.1:6379> auth SbN8nBfMWh OK 127.0.0.1:6379> info replication # Replication role:master connected_slaves:2 slave0:ip=172.30.104.11,port=6379,state=online,offset=350,lag=1 slave1:ip=172.30.160.8,port=6379,state=online,offset=350,lag=0 master_replid:60809f9cc34104686610bab1a41d727382f50ac0 master_replid2:0000000000000000000000000000000000000000 master_repl_offset:350 second_repl_offset:-1 repl_backlog_active:1 repl_backlog_size:1048576 repl_backlog_first_byte_offset:1 repl_backlog_histlen:350 127.0.0.1:6379>
- k8s-181 从节点0
[root@k8s-181 ~]# kubectl exec -it redis-slave-0 redis-cli 127.0.0.1:6379> auth SbN8nBfMWh OK 127.0.0.1:6379> info replication # Replication role:slave master_host:redis-master-0.redis-headless.default.svc.cluster.local master_port:6379 master_link_status:up master_last_io_seconds_ago:1 master_sync_in_progress:0 slave_repl_offset:504 slave_priority:100 slave_read_only:1 connected_slaves:0 master_replid:60809f9cc34104686610bab1a41d727382f50ac0 master_replid2:0000000000000000000000000000000000000000 master_repl_offset:504 second_repl_offset:-1 repl_backlog_active:1 repl_backlog_size:1048576 repl_backlog_first_byte_offset:29 repl_backlog_histlen:476 127.0.0.1:6379>
- k8s-182,从节点1
[root@k8s-181 ~]# kubectl exec -it redis-slave-1 redis-cli 127.0.0.1:6379> auth SbN8nBfMWh OK 127.0.0.1:6379> info replication # Replication role:slave master_host:redis-master-0.redis-headless.default.svc.cluster.local master_port:6379 master_link_status:up master_last_io_seconds_ago:8 master_sync_in_progress:0 slave_repl_offset:588 slave_priority:100 slave_read_only:1 connected_slaves:0 master_replid:60809f9cc34104686610bab1a41d727382f50ac0 master_replid2:0000000000000000000000000000000000000000 master_repl_offset:588 second_repl_offset:-1 repl_backlog_active:1 repl_backlog_size:1048576 repl_backlog_first_byte_offset:1 repl_backlog_histlen:588 127.0.0.1:6379>
可以看出172.30.104.11和172.30.160.8这两个节点作为了从节点
从节点断开和主节点的同步
通过slaveof
我在k8s-181节点上断开和主节点的同步,可以观察到断开连接后从节点又变回为主节点
再看k8s-180节点,从以前的两个从节点连接变成了一个
再次连接上主节点
使用 slaveof host port再次连接上主节点
127.0.0.1:6379> slaveof 172.30.104.10 6379 OK
只读库
一般来说线上的redis环境,从库是只读的.主要是由于
- 从库的更改,主库不能及时感知.可能会造成数据不一致
可以使用config get slave-read-only 来查看从库是否开启了只读模式,从下面的命令可以看到.我这里的从库是只读的
127.0.0.1:6379> config get slave-read-only 1) "slave-read-only" 2) "yes" 127.0.0.1:6379>
延迟
127.0.0.1:6379> config get repl-disable-tcp-nodelay 1) "repl-disable-tcp-nodelay" 2) "no" 127.0.0.1:6379>
repl-disable-tcp-nodelay no:该配置作用于命令传播阶段,控制主节点是否禁止与从节点的TCP_NODELAY;默认no,即不禁止TCP_NODELAY。当设置为yes时,TCP会对包进行合并从而减少带宽,但是发送的频率会降低,从节点数据延迟增加,一致性变差;具体发送频率与Linux内核的配置有关,默认配置为40ms。当设置为no时,TCP会立马将主节点的数据发送给从节点,带宽增加但延迟变小。
一般来说,只有当应用对Redis数据不一致的容忍度较高,且主从节点之间网络状况不好时,才会设置为yes;多数情况使用默认值no。
主从复制的过程
建立TCP连接
复制的前提是主从节点之间能感知到对方的存在,需要知道对方的IP和端口
- 从节点感知主节点的IP和端口
由于我们在配置或者使用命令连接上主库时,需要指定主库的IP和端口.这样从库便知道了主库的IP和端口(个人理解,不一定对)
从节点服务器内部维护了两个字段,即masterhost和masterport字段,用于存储主节点的ip和port信息。 - 建立socket连接
从节点调用复制定时函数replicationCron()来尝试连接到主节点 发送ping命令
从节点成为主节点的客户端之后,发送ping命令进行首次请求,目的是:检查socket连接是否可用,以及主节点当前是否能够处理请求。
从节点发送ping命令后,可能出现3种情况:
(1)返回pong:说明socket连接正常,且主节点当前可以处理请求,复制过程继续。
(2)超时:一定时间后从节点仍未收到主节点的回复,说明socket连接不可用,则从节点断开socket连接,并重连。
(3)返回pong以外的结果:如果主节点返回其他结果,如正在处理超时运行的脚本,说明主节点当前无法处理命令,则从节点断开socket连接,并重连。身份验证
如果从节点中设置了masterauth选项,则从节点需要向主节点进行身份验证;没有设置该选项,则不需要验证
127.0.0.1:6379> config get masterauth 1) "masterauth" 2) "" 127.0.0.1:6379>
在我这里没有为主节点设置masterauth
- 发送从节点端口信息
身份验证之后,从节点会向主节点发送其监听的端口号.主节点将该信息保存到该从节点对应的客户端的slave_listening_port字段中;
数据同步阶段
在Redis2.8及以后,从节点可以发送psync命令请求同步数据,此时根据主从节点当前状态的不同,同步方式可能是全量复制或部分复制
psync命令运行需要以下组件支持:
A.主Redis的复制偏移量(replication offset)和从Redis的复制偏移量。
B.主Redis的复制积压缓冲区(replication backlog)。
C.Redis的运行ID(run ID)。
run_id,offset,复制积压缓冲区
- run_id:用来标识redis实例
Redis 服务器的随机标识符(用于 Sentinel 和集群),重启后就会改变;当复制时发现和之前的 run_id 不同时,将会对数据全量同步。 - 复制偏移量:通过对比主从节点的复制偏移量,可以判断主从节点数据是否一致。
1.参与复制的主从节点都会维护自身复制偏移量。主节点(master)在处理完写入命令后,会把命令的字节长度做累加记录.
2.从节点(slave)每秒钟上报自身的复制偏移量给主节点,因此主节点也会保存从节点的复制偏移量.
3.从节点在接收到主节点发送的命令后,也会累加记录自身的偏移量 - 复制积压缓冲区
127.0.0.1:6379> config get repl-backlog-size 1) "repl-backlog-size" 2) "1048576"
复制积压缓冲区是由主服务器维护的一个固定长度(fixed-size)先进先出(FIFO)队列,默认大小为1MB。
当主服务器进行命令传播时,它不仅会将写命令发送给所有从服务器,还会将写命令入队到复制积压缓冲区里面
因此,主服务器的复制积压缓冲区里面会保存着一部分最近传播的写命令,并且复制积压缓冲区会为队列中的每个字节记录相应的复制偏移量,就像下表所示的那样。
当从服务器重新连上主服务器时,从服务器会通过PSYNC命令将自己的复制偏移量offset发送给主服务器,主服务器会根据这个复制偏移量来决定对从服务器执行何种同步操作:
a、如果offset偏移量之后的数据(也即是偏移量offset+1开始的数据)仍然存在于复制积压缓冲区里面,那么主服务器将对从服务器执行部分重同步操作;
b、相反,如果offset偏移量之后的数据已经不存在于复制积压缓冲区,那么主服务器将对从服务器执行完整重同步操作。
举例: 从服务器偏移量为10086,如果能在积压缓冲区里找到10086+1的数据,执行部分同步.由于队列长度有限,可能已经超对队列.
全量复制:
一般用于初次复制场景,Redis早期支持的复制功能只有全量复制,它会把主节点全部数据一次性发送给从节点,当数据量较大时,会对主从节点和网络造成很大的开销。
1.Redis内部会发出一个同步命令,刚开始是Psync命令,Psync ? -1表示要求master主机同步数据
2.主机会向从机发送run_id和offset,因为slave并没有对应的 offset,所以是全量复制
3.从机slave会保存主机master的基本信息
4.主节点收到全量复制的命令后,执行bgsave(异步执行),在后台生成RDB文件(快照),并使用一个缓冲区(称为复制缓冲区)记录从现在开始执行的所有写命令
5.主机发送RDB文件给从机
6.发送缓冲区数据
7.刷新旧的数据。从节点在载入主节点的数据之前要先将老数据清除
8.加载RDB文件将数据库状态更新至主节点执行bgsave时的数据库状态和缓冲区数据的加载。
部分复制:
用于网络中断等情况后的复制,只将中断期间主节点执行的写命令发送给从节点,与全量复制相比更加高效。需要注意的是,如果网络中断时间过长,导致主节点没有能够完整地保存中断期间执行的写命令,则无法进行部分复制,仍使用全量复制。
1.如果网络抖动(连接断开 connection lost)
2.主机master 还是会写 repl_back_buffer(复制缓冲区)
3.从机slave 会继续尝试连接主机
4.从机slave 会把自己当前 run_id 和偏移量传输给主机 master,并且执行 pysnc 命令同步
5.如果master发现你的偏移量是在缓冲区的范围内,就会返回 continue命令
同步了offset的部分数据,所以部分复制的基础就是偏移量 offset。
引用链接
https://www.iteye.com/blog/uule-2429825