这是我参与「第五届青训营 」伴学笔记创作活动的第 15 天
今天我学习了字节跳动青训营内部课程的数据库与存储,我学习了关于数据库主从架构的内容
一、搭建主从架构
为什么要搭建
如果服务器发生了宕机,由于数据恢复是需要点时间,那么这个期间是无法服务新的请求的 如果这台服务器的硬盘出现了故障,可能数据就都丢失了 要避免这种单点故障,最好的办法是将数据备份到其他服务器上,让这些服务器也可以对外提供服务,这样即使有一台服务器出现了故障,其他服务器依然可以继续提供服务。
单节点Redis的并发能力是有上限的,要进一步提高Redis的并发能力,就需要搭建主从集群,实现读写分离
二、 数据同步原理
1、全量同步
全量同步只有第一次的时候才会发送 master如何判断slave是不是第一次来同步数据呢?
判断replid数据集的标记,id一致则说明是同一数据集,每一个master都有唯一的replid,slave则会继承master节点的replid,第一次来主会把id同步给从节点 offset偏移量,随着记录在repl——baklog中的数据增多,slave完成同步时也会记录当前同步的offset。如果slave的offset小于master的offset,说明slave数据落后于master,需要更新 因此slave做数据同步,必须向master声明自己的replication id和offset,master才可以判断到底需要同步哪些数据
详细解析步骤
第一阶段:建立链接、协商同步 执行了 replicaof 命令后,从服务器就会给主服务器发送 psync 命令,表示要进行数据同步。 psync 命令包含两个参数,分别是主服务器的 runID 和复制进度 offset。 runID,每个 Redis 服务器在启动时都会自动生产一个随机的 ID 来唯一标识自己。当从服务器和主服务器第一次同步时,因为不知道主服务器的 run ID,所以将其设置为 "?"。 offset,表示复制的进度,第一次同步时,其值为 -1。 主服务器收到 psync 命令后,会用 FULLRESYNC 作为响应命令返回给对方。 并且这个响应命令会带上两个参数:主服务器的 runID 和主服务器目前的复制进度 offset。从服务器收到响应后,会记录这两个值。 FULLRESYNC 响应命令的意图是采用全量复制的方式,也就是主服务器会把所有的数据都同步给从服务器。 所以,第一阶段的工作时为了全量复制做准备。 那具体怎么全量同步呀呢?我们可以往下看第二阶段。
第二阶段:主服务器同步数据给从服务器 接着,主服务器会执行 bgsave 命令来生成 RDB 文件,然后把文件发送给从服务器。 从服务器收到 RDB 文件后,会先清空当前的数据,然后载入 RDB 文件。 这里有一点要注意,主服务器生成 RDB 这个过程是不会阻塞主线程的,因为 bgsave 命令是产生了一个子进程来做生成 RDB 文件的工作,是异步工作的,这样 Redis 依然可以正常处理命令。 但是,这期间的写操作命令并没有记录到刚刚生成的 RDB 文件中,这时主从服务器间的数据就不一致了。 那么为了保证主从服务器的数据一致性,主服务器在下面这三个时间间隙中将收到的写操作命令,写入到 replication buffer 缓冲区里: 主服务器生成 RDB 文件期间; 主服务器发送 RDB 文件给从服务器期间; 「从服务器」加载 RDB 文件期间;
第三阶段:主服务器发送新写操作命令给从服务器 在主服务器生成的 RDB 文件发送完,从服务器收到 RDB 文件后,丢弃所有旧数据,将 RDB 数据载入到内存。完成 RDB 的载入后,会回复一个确认消息给主服务器。 接着,主服务器将 replication buffer 缓冲区里所记录的写操作命令发送给从服务器,从服务器执行来自主服务器 replication buffer 缓冲区里发来的命令,这时主从服务器的数据就一致了。 至此,主从服务器的第一次同步的工作就完成了。
2、命令传播
主从服务器在完成第一次同步后,双方之间就会维护一个 TCP 连接。 后续主服务器可以通过这个连接继续将写操作命令传播给从服务器,然后从服务器执行该命令,使得与主服务器的数据库状态相同。 而且这个连接是长连接的,目的是避免频繁的 TCP 连接和断开带来的性能开销。 上面的这个过程被称为基于长连接的命令传播,通过这种方式来保证第一次同步后的主从服务器的数据一致性
3、增量同步
一般从节点重启之后会做增量同步,从节点突然断开了一段时间又不可能重新全量同步性能太低
repl_baklog大小是有上限的,写满后会覆盖最早的数据,如果slave断开太久,导致未备份的数据被覆盖了,则无法基于log增量同步,只能再次全量同步 优化Redis主从:
在master中配置repl-diskless-sync yes启动无磁盘赋值,避免全量同步时的磁盘IO(全量同步写入RDB文件时候是写入磁盘的效率太低了,我们配置写入网络然后直接发给从)提高全量同步性能 Redis单节点上的内存占用不要太大,减少RDB导致的过多磁盘IO(不用写太多)提高全量同步性能角度 适当提高repl_baklog的大小,发现slave宕机时尽快实现故障恢复,尽可能避免全量同步 限制一个master的slave节点数量,如果实在太多slave,则可以采用主从从链式结构,减少master压力
增量详细步骤
从服务器在恢复网络后,会发送 psync 命令给主服务器,此时的 psync 命令里的 offset 参数不是 -1;
主服务器收到该命令后,然后用 CONTINUE 响应命令告诉从服务器接下来采用增量复制的方式同步数据;
然后主服务将主从服务器断线期间,所执行的写命令发送给从服务器,然后从服务器执行这些命令。
那么关键的问题来了,主服务器怎么知道要将哪些增量数据发送给从服务器呢?
答案藏在这两个东西里: repl_backlog_buffer,是一个「环形」缓冲区,用于主从服务器断连后,从中找到差异的数据; replication offset,标记上面那个缓冲区的同步进度,主从服务器都有各自的偏移量,主服务器使用 master_repl_offset 来记录自己「写」到的位置,从服务器使用 slave_repl_offset 来记录自己「读」到的位置。
那repl_backlog_buffer 缓冲区是什么时候写入的呢?
在主服务器进行命令传播时,不仅会将写命令发送给从服务器,还会将写命令写入到 repl_backlog_buffer 缓冲区里,因此 这个缓冲区里会保存着最近传播的写命令。
网络断开后,当从服务器重新连上主服务器时,从服务器会通过 psync 命令将自己的复制偏移量 slave_repl_offset 发送给主服务器,主服务器根据自己的 master_repl_offset 和 slave_repl_offset 之间的差距,然后来决定对从服务器执行哪种同步操作: 如果判断出从服务器要读取的数据还在 repl_backlog_buffer 缓冲区里,那么主服务器将采用增量同步的方式; 相反,如果判断出从服务器要读取的数据已经不存在 repl_backlog_buffer 缓冲区里,那么主服务器将采用全量同步的方式。
当主服务器在 repl_backlog_buffer 中找到主从服务器差异(增量)的数据后,就会将增量的数据写入到 replication buffer 缓冲区,这个缓冲区我们前面也提到过,它是缓存将要传播给从服务器的命令。
repl_backlog_buffer 缓行缓冲区的默认大小是 1M,并且由于它是一个环形缓冲区,所以当缓冲区写满后,主服务器继续写入的话,就会覆盖之前的数据。因此,当主服务器的写入速度远超于从服务器的读取速度,缓冲区的数据一下就会被覆盖。
那么在网络恢复时,如果从服务器想读的数据已经被覆盖了,主服务器就会采用全量同步,这个方式比增量同步的性能损耗要大很多。
因此,为了避免在网络恢复时,主服务器频繁地使用全量同步的方式,我们应该调整下 repl_backlog_buffer 缓冲区大小,尽可能的大一些,减少出现从服务器要读取的数据被覆盖的概率,从而使得主服务器采用增量同步的方式。