前言
📚 全文字数 : 8k
⏳ 阅读时长 : 12min
📢 关键词 : Redis高可用、主从复制、psync
概念
在之前的Redis持久化文章【# 图说Redis持久化 RDB和AOF,我终于全明白了!】, 通过学习我们知道通过持久化技术让服务器重启的情况下尽可能少或者不会丢失数据。
但是问题在于持久化的数据在单一的服务器上,万一服务器的硬盘出现了故障,那就可能数据就真的没了!
而在Redis中主从复制,是指将一台Redis服务器的数据,复制到其他的Redis服务器(数据备份了)。被复制的服务器称为主服务器(master ),对主服务器进行复制操作的为从服务器(slave)。
🚩 要注意的是数据的复制是单向的,只能由主节点到从节点!
主从服务器库之间采用的是读写分离的方式
- 读操作:主库、从库都可以接收读操作
- 写操作:首先到主库执行写,然后,主库将写操作同步给从库
主从复制的好处
- 数据冗余 :实现数据的热备份
- 故障恢复 : 避免单点故障带来的服务不可用,可以由从节点提供服务,实现快速的故障恢复
- 读写分离 : 在主从复制的基础上,配合读写分离,可以由主节点提供写服务,由从节点提供读服务,可分担服务器负载;尤其是在写少读多的场景下,通过多个从节点分担读负载,可以大大提高Redis服务器的并发量
- 高可用基础 :是哨兵机制和集群实现的基础
今天小许将分享Redis高可用知识点之【Redis主从复制】,可能你在其他地方看过,相信你跟着小许思路,能帮你重新回忆一遍、不会的同学好好学一波!
内容比较多,在地铁上看文章的你可以先关注、收藏一下,用电脑看舒服!
欢迎朋友们关注我的同名公众号📢📢:【小许code】,等你哦!🤣🤣
实现原理
进行复制之前我们要确定的是谁是主和从服务器,我们将在从从服务器上使用 slaveof命令形成主从关系,命令如下:
redis 5.0之前使用 salveof 命令(salveof <master IP 地址> <master 端口号>)
redis 5.0之后使用 replicaof 命令(replicaof <master IP 地址> <master 端口号>)
文章后续将用 master 表示主服务器 , slave 表示从服务器。
Redis服务器执行上述命令的成为了从服务器slave,我们看下进行复制涉及了哪些流程,然后一个个看看每个流程具体干了什么,了解Master和Slave复制的实现!
设置master 地址和端口
在准备成为slave的Redis服务器上执行下面命令:
ruby
127.0.0.1:12345> SLAVEOF 127.0.0.1 6379
OK
salve服务器执行salveof命令【异步命令】的目的是,将给定的master服务器IP地址127.0.0.1和端口6379,保存到从服务器的masterhost属性和masterport属性里面。
redisServer 结构体的字段太多了,这里只展示复制是master的一部分,还有slave的部分属性就不展示了!
arduino
//salve的服务器结构体值
struct redisServer{
//...
/* Replication (slave) */
// 验证信息
char *masterauth;
// 命令执行成功后值 127.0.0.1
char *masterhost;
// 6379
int masterport;
//...
};
建立socket连接
slave将根据命令(slave或replicaof)设置的IP地址和端口,创建连向master的套接字(socket)连接。
socket连接到master之后,会注册一个文件事件【syncWithMaster】,接收后续RDB文件、实时写命令,当然在连接建立之后,master也会创建相应的客户端状态啦!
发送 ping 命令
slave成为mater的客户端之后先发送PING命令,主要作用如下:
- 检查salve和master的套接字读写状态
- 检查master是否能正常处理命令
slave收到PONG回复后才会继续执行后续步骤!
身份验证
客户端如果开启了密码保护的话,在每次连接 Redis 服务器之后,就要使用 AUTH 命令解锁,解锁之后才能使用其他 Redis 命令。
也就是说如果slave设置了 masterauth 属性,那么将会向master发送一条AUTH命令进行身份验证,目的是检验给定的密码 password 和master配置文件中requirepass 项的值是否相符。
向master发送监听端口
slave身份验证之后,发送执行以下命令向mater发送监听端口信息
xml
REPLCONF listening-port <salve监听端口号>
master接收到到命令后,会记录在主服务器对应的客户端状态 salve_listening_port属性中
我们可以在master上执行"info replication"命令可以看到从服务器的port
同步
slave会向master发送一个 psync 命令来进行数据同步,并且数据同步将分为全量同步和部分同步。
命令持续复制
在完成同步之后,master -- slave 会进入到命令传播阶段、这个阶段master将写命令发送给slave,slave接收并执行增量的命令同步实现主从一致。
画个图总结一下实现原理的流程吧:
👉 怎么判断slave是第一次进行主从复制呢?
Redis核心结构server的cached_master保存了master节点的信息,只有进行过主从复制才会赋值,否则为空。
复制方式
Redis复制的方式可分为全量复制和增量复制,不过在第一次全量复制之后,master和slave双方之间就会维护一个 TCP 长连接,后续master可以通过这个连接继续将新写操作命令同步给slave。
全量复制
全量复制意思是master当前全部数据进行复制同步到slave。
全量复制可分为三个阶段,我们先看下图有个整体印象,然后再看每个阶段都干了啥!
📢 阶段一 建立链接、协商同步
这部分流程我们在【实现原理】章节讲比较明白了,这个阶段建立连接,并告诉master需要进行同步,等确认后就可以为后续的全量复制做准备。
具体来说就是在 slave给master发送 psync 命令,表示要进行数据同步,master会根据这个命令的参数来启动复制!
👉 psync ? -1,命令参数 '?'和 '-1'是什么意思?
这两个参数分别表示 master 的唯一表示 runID 和复制进度 offset ,因为是第一次复制,此时是不知道master的runID的,所以设置' ?',而 -1 表示第一次复制。
注:每个 Redis 实例启动时都会自动生成的一个随机 ID,这个就是runID
master在收到 psync 命令之后会用 FULLRESYNC 响应命令带上两个参数:master 的runID 和的复制进度 offset,这个时候 salve 就可以记下这两个参数值了。
FULLRESYNC : 意思是完全重新同步,就是会把当前master的所有数据复制同步给salve
📢📢 阶段是二 master同步数据给从slave
master 收到 psync 命令后,会执行bgsave命令, fork 出一个子进程,子进程中将所有的数据按特定编码存储到 RDB(Redis Database) 文件中。
RDB生成完成之后就会把文件发送给salve,从库接收到RDB文件后,先清空当前数据库数据,然后才会加载RDB文件。
👉 salve在加载RDB前清除数据的目的是什么?
salve 在执行replicaof 或 slaveof 命令开始复制前,可能保存了其他数据,清除是为了避免之前数据的影响
👉 master在执行bgsave期间新写的命令并没有生成到RDB中,数据丢失了吗?
生成RDB的是fork出的子进程做的,此时主进程还是可以正常处理写入命令的,此时为了保证主从的数据一致性master 会用专门的 replication buffer 来记录 RDB 文件生成、加载RDB文件期间收到的所有写操作。
📢📢📢 阶段三 master发送新写操作命令给slave
master会把阶段二期间记录在 replication buffer 的写命令,发送给salve,通过这种方式来实现主从的同步一致性。
👉 在什么情况下会进行全量同步呢?
- slave连接上master第一次复制的时候
- 从节点发送 psync {runid} {offset} 时,runid 与当前主节点的 runid 不匹配则进行全量复制
- 从节点所需要同步数据的偏移量 offset 不在复制积压缓冲区中
命令传播
master在完成第一次同步后,就会基于长连接进行后续命令传播,master通过这个连接将写命令传播给slave,salve执行得到的写命令,从而保正主从数据的同步。
增量复制
我们知道在第一次全量复制后,主从之间使用长连接进行命令传播,但是如果网络出现问题,出现闪断(断了一会又恢复了)那麻烦了,用户可能从salve读到旧数据。
这种情况下Redis 2.8开始会采用增量复制的方式继续同步,如下图流程:
问题的关键在于如何知道哪些数据作为增量发送给slave,在分析之前我们先了解几个概念:
- 复制偏移量 (replication offset)
- 复制积压缓冲区 (replication backlog)
- 服务器运行ID (runID)
📢 复制偏移量 ( replication offset)
主从节点都维护这一个复制偏移量,它代表着当前节点接受数据的字节数,注意这里表示的是【字节数】
上图中master和slave的offset一开始是相等的,可以理解为数据一致,但是master此时增加了6个字节数据,此时需要向salve传播长度为6字节的数据,从而保持主从数据的一致,offset一致。
⏰ 想要知道主从是否一致,通过对比offset,也能知道
📢📢 复制积压缓冲区 ( replication backlog buffer )
复制积压缓冲区是由master维护的一个固定长度的环形缓冲区、是先进先出(FIFO)队列,默认大小为 1MB。
在命令传播阶段,除了发送命令给salve之外,还会写入到复制积压缓冲区,此时salve还会做一个重要的事情,发送 REPLCONF ACK 命令给master,传递 replication_offset(slave当前的复制偏移量),这个命令有三个作用:
- 检测主从服务器的网络状态
- 辅助实现 min-slaves 选项
- 检测命令丢失
📢📢📢 服务器运行ID (runID)
不论主从都会有自己的运行ID,在Redis服务器启动时会自动生成,由40个随机16进制字符组成,第一次复制master会发送这个ID给到slave,而断线重连时slave会带上这个ID发送给master!
了解这些知识后,我们再理一理,在网络短暂断开后,salve重新连上master时,salve会通过 psync 命令将自己的复制偏移量 offset 发送给master,而master根据自己的offset 和 slave的offset 之间的差距,然后来决定对salve执行哪种同步操作。
- 判断出salve要读取的数据还在 replication_backlog_buffer 里,那么主服务器将采用增量同步的方式;
- 判断出读取的数据已经不存在 replication_backlog_buffer 里 (比如被覆盖掉了),那么主服务器将采用全量同步的方式
👉 replication_backlog_buffer 大小只有1M,数据被覆盖的概率挺大,该如何配置避免呢?
replication_backlog_buffer 最小的大小可以这样估算
repl_backlog_buffer = second * write_size_per_second
- second 为salve断线后重新连接上master所需的平均 时间(以秒计算)。
- write_size_per_second 则是master平均每秒产生的写命令数据量大小
这个配置我们看情况去定,这个参数在配置文件中如下,我们可以去修改它
arduino
repl-backlog-size 1mb
主从复制配置
配置方式
进行配置主从复制还是比较简单的,可以用 两种方式:
- 在从服务器中添加配置 slaveof 选项,不过在5.0版本中使用了replicaof 代替了slaveof,虽然 slaveof可以继续使用,建议使用 replicaof
- 直接使用 slaveof 命令
arduino
# redis.conf文件进行主从配置
# replicaof <masterip> <masterport>
replicaof 192.168.127.20 6379
配置好 redis.conf之后,我们分别启动主从服务器,可以用户命令 info replication 查看复制信息
AUTH设置密码
如果需要在 slave对master的建立连接是进行验证,可以在master中配置requirepass 选项设置密码
那么需要在从服务器salve中使用该密码,可以使用命令config set masterauth ,或者在配置文件中设置masterauth
总结
进行主从复制之前需要master和slave进行连接,只有连接成功之后才能进行后续的复制动作。
在主从服务器发送 pysnc 进行第一次同步的时候,采用的是全量复制,而在同步完成之后,主从服务器都会维护着一个长连接,主服务器在接收到新的写操作命令后,通过这个连接发送给从服务器,从而保持数据的一致性。
如果遇到网络闪断情况,此时就进行增量复制,不过需要确定复制积压缓冲区是否覆盖等情况才决定是进行全量还是增量复制
欢迎点赞 👍、收藏 💙、关注 💡 三连支持一下~🎈
🎈知道的越多,不知道的也越多,我是小许,下期见~🙇💻