Redis 高可用篇:你管这叫主从架构数据同步原理? (上)

本文涉及的产品
云数据库 Tair(兼容Redis),内存型 2GB
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
简介: 本篇主要带大家全方位吃透 Redis 高可用技术解决方案之一主从复制架构。本篇硬核,建议收藏慢慢品味,我相信读者朋友会有一个质的提升。如有错误还望纠正,谢谢。关注「码哥字节」设置「星标」第一时间接收优质文章,谢谢读者的支持。

高可用有两个含义:一是数据尽量不丢失,二是服务尽可能提供服务。 AOF 和 RDB 保证了数据持久化尽量不丢失,而主从复制就是增加副本,一份数据保存到多个实例上。即使有一个实例宕机,其他实例依然可以提供服务。


核心知识点


image.png


开篇寄语


问题 = 机会。遇到问题的时候,内心其实是开心的,越大的问题意味着越大的机会。

任何事情都是有代价的,有得必有失,有失必有得,所以不必计较很多东西,我们只要想清楚自己要做什么,并且想清楚自己愿意为之付出什么代价,然后就放手去做吧!


1. 主从复制概述


65 哥:有了 RDB 和 AOF 再也不怕宕机丢失数据了,但是 Redis 实例宕机了怎么实现高可用呢?


既然一台宕机了无法提供服务,那多台呢?是不是就可以解决了。Redis 提供了主从模式,通过主从复制,将数据冗余一份复制到其他 Redis 服务器。


前者称为主节点 (master),后者称为从节点 (slave);数据的复制是单向的,只能由主节点到从节点。


默认情况下,每台 Redis 服务器都是主节点;且一个主节点可以有多个从节点 (或没有从节点),但一个从节点只能有一个主节点。


65 哥:主从之间的数据如何保证一致性呢?


为了保证副本数据的一致性,主从架构采用了读写分离的方式。


  • 读操作:主、从库都可以执行;


  • 写操作:主库先执行,之后将写操作同步到从库;


image.png


65 哥:为何要采用读写分离的方式?


我们可以假设主从库都可以执行写指令,假如对同一份数据分别修改了多次,每次修改发送到不同的主从实例上,就导致是实例的副本数据不一致了。


如果为了保证数据一致,Redis 需要加锁,协调多个实例的修改,Redis 自然不会这么干!


65 哥:主从复制还有其他作用么?


  1. 故障恢复:当主节点宕机,其他节点依然可以提供服务;


  1. 负载均衡:Master 节点提供写服务,Slave 节点提供读服务,分担压力;


  1. 高可用基石:是哨兵和 cluster 实施的基础,是高可用的基石。


2. 搭建主从复制


主从复制的开启,完全是在从节点发起的,不需要我们在主节点做任何事情。


65 哥:怎么搭建主从复制架构呀?


可以通过 replicaof(Redis 5.0 之前使用 slaveof)命令形成主库和从库的关系。


在从节点开启主从复制,有 3 种方式:


  1. 配置文件


在从服务器的配置文件中加入 replicaof <masterip> <masterport>


  1. 启动命令


redis-server 启动命令后面加入 --replicaof <masterip> <masterport>


  1. 客户端命令


启动多个 Redis 实例后,直接通过客户端执行命令:replicaof <masterip> <masterport>,则该 Redis 实例成为从节点。


比如假设现在有实例 1(172.16.88.1)、实例 2(172.16.88.2)和实例 3 (172.16.88.3),在实例 2 和实例 3 上分别执行以下命令,实例 2 和 实例 3 就成为了实例 1 的从库,实例 1 成为 Master。


replicaof 172.16.88.1 6379


3. 主从复制原理


主从库模式一旦采用了读写分离,所有数据的写操作只会在主库上进行,不用协调三个实例。


主库有了最新的数据后,会同步给从库,这样,主从库的数据就是一致的。


65 哥:主从库同步是如何完成的呢?主库数据是一次性传给从库,还是分批同步?正常运行中又怎么同步呢?要是主从库间的网络断连了,重新连接后数据还能保持一致吗?


65 哥你问题咋这么多,同步分为三种情况:


  1. 第一次主从库全量复制;


  1. 主从正常运行期间的同步;


  1. 主从库间网络断开重连同步。


主从库第一次全量复制


65 哥:我好晕啊,先从主从库间第一次同步说起吧。


主从库第一次复制过程大体可以分为 3 个阶段:连接建立阶段(即准备阶段)、主库同步数据到从库阶段、发送同步期间新写命令到从库阶段


直接上图,从整体上有一个全局观的感知,后面具体介绍。


image.png


建立连接


该阶段的主要作用是在主从节点之间建立连接,为数据全量同步做好准备。从库会和主库建立连接,从库执行 replicaof 并发送 psync 命令并告诉主库即将进行同步,主库确认回复后,主从库间就开始同步了


65 哥:从库怎么知道主库信息并建立连接的呢?


在从节点的配置文件中的 replicaof 配置项中配置了主节点的 IP 和 port 后,从节点就知道自己要和那个主节点进行连接了。


从节点内部维护了两个字段,masterhost 和 masterport,用于存储主节点的 IP 和 port 信息。


从库执行 replicaof 并发送 psync 命令,表示要执行数据同步,主库收到命令后根据参数启动复制。命令包含了主库的 runID复制进度 offset 两个参数。


  • runID:每个 Redis 实例启动都会自动生成一个 唯一标识 ID,第一次主从复制,还不知道主库 runID,参数设置为 「?」。


  • offset:第一次复制设置为 -1,表示第一次复制,记录复制进度偏移量。


主库收到 psync 命令后,会用 FULLRESYNC 响应命令带上两个参数:主库 runID 和主库目前的复制进度 offset,返回给从库。从库收到响应后,会记录下这两个参数。


FULLRESYNC 响应表示第一次复制采用的全量复制,也就是说,主库会把当前所有的数据都复制给从库。


主库同步数据给从库


第二阶段


master 执行 bgsave命令生成 RDB 文件,并将文件发送给从库,同时主库为每一个 slave 开辟一块 replication buffer 缓冲区记录从生成 RDB 文件开始收到的所有写命令。


从库收到 RDB 文件后保存到磁盘,并清空当前数据库的数据,再加载 RDB 文件数据到内存中。


发送新写命令到从库


第三阶段


从节点加载 RDB 完成后,主节点将 replication buffer 缓冲区的数据发送到从节点,Slave 接收并执行,从节点同步至主节点相同的状态。


65 哥:主库将数据同步到从库过程中,可以正常接受请求么?


主库不会被阻塞,Redis 作为唯快不破的男人,怎么会动不动就阻塞呢。


在生成 RDB 文件之后的写操作并没有记录到刚刚的 RDB 文件中,为了保证主从库数据的一致性,所以主库会在内存中使用一个叫 replication buffer 记录 RDB 文件生成后的所有写操作。


65 哥:为啥从库收到 RDB 文件后要清空当前数据库?


因为从库在通过 replcaof命令开始和主库同步前可能保存了其他数据,防止主从数据之间的影响。


replication buffer 到底是什么玩意?


一个在 master 端上创建的缓冲区,存放的数据是下面三个时间内所有的 master 数据写操作。


1)master 执行 bgsave 产生 RDB 的期间的写操作;


2)master 发送 rdb 到 slave 网络传输期间的写操作;

3)slave load rdb 文件把数据恢复到内存的期间的写操作。


Redis 和客户端通信也好,和从库通信也好,Redis 都分配一个内存 buffer 进行数据交互,客户端就是一个 client,从库也是一个 client,我们每个 client 连上 Redis 后,Redis 都会分配一个专有 client buffer,所有数据交互都是通过这个 buffer 进行的。


Master 先把数据写到这个 buffer 中,然后再通过网络发送出去,这样就完成了数据交互。


不管是主从在增量同步还是全量同步时,master 会为其分配一个 buffer ,只不过这个 buffer 专门用来传播写命令到从库,保证主从数据一致,我们通常把它叫做 replication buffer。


replication buffer 太小会引发的问题


replication buffer 由 client-output-buffer-limit slave 设置,当这个值太小会导致主从复制连接断开


1)当 master-slave 复制连接断开,master 会释放连接相关的数据。replication buffer 中的数据也就丢失了,此时主从之间重新开始复制过程。


2)还有个更严重的问题,主从复制连接断开,导致主从上出现重新执行 bgsave 和 rdb 重传操作无限循环。


当主节点数据量较大,或者主从节点之间网络延迟较大时,可能导致该缓冲区的大小超过了限制,此时主节点会断开与从节点之间的连接;


这种情况可能引起全量复制 -> replication buffer 溢出导致连接中断 -> 重连 -> 全量复制 -> replication buffer 缓冲区溢出导致连接中断……的循环。


因而推荐把 replication buffer 的 hard/soft limit 设置成 512M。


config set client-output-buffer-limit "slave 536870912 536870912 0"


65 哥:主从库复制为何不使用 AOF 呢?相比 RDB 来说,丢失的数据更少。


这个问题问的好,原因如下:


  1. RDB 文件是二进制文件,网络传输 RDB 和写入磁盘的 IO 效率都要比 AOF 高。


  1. 从库进行数据恢复的时候,RDB 的恢复效率也要高于 AOF。


增量复制


65 哥:主从库间的网络断了咋办?断开后要重新全量复制么?


在 Redis 2.8 之前,如果主从库在命令传播时出现了网络闪断,那么,从库就会和主库重新进行一次全量复制,开销非常大。


从 Redis 2.8 开始,网络断了之后,主从库会采用增量复制的方式继续同步。


增量复制:用于网络中断等情况后的复制,只将中断期间主节点执行的写命令发送给从节点,与全量复制相比更加高效


repl_backlog_buffer


断开重连增量复制的实现奥秘就是 repl_backlog_buffer 缓冲区,不管在什么时候 master 都会将写指令操作记录在 repl_backlog_buffer 中,因为内存有限, repl_backlog_buffer 是一个定长的环形数组,如果数组内容满了,就会从头开始覆盖前面的内容


master 使用 master_repl_offset记录自己写到的位置偏移量,slave 则使用 slave_repl_offset记录已经读取到的偏移量。


master 收到写操作,偏移量则会增加。从库持续执行同步的写指令后,在 repl_backlog_buffer 的已复制的偏移量 slave_repl_offset 也在不断增加。


正常情况下,这两个偏移量基本相等。在网络断连阶段,主库可能会收到新的写操作命令,所以 master_repl_offset会大于 slave_repl_offset


image.png


当主从断开重连后,slave 会先发送 psync 命令给 master,同时将自己的 runIDslave_repl_offset发送给 master。


master 只需要把 master_repl_offsetslave_repl_offset之间的命令同步给从库即可。


增量复制执行流程如下图:


image.png


65 哥:repl_backlog_buffer 太小的话从库还没读取到就被 Master 的新写操作覆盖了咋办?


我们要想办法避免这个情况,一旦被覆盖就会执行全量复制。我们可以调整 repl_backlog_size 这个参数用于控制缓冲区大小。计算公式:


repl_backlog_buffer = second * write_size_per_second


  1. second:从服务器断开重连主服务器所需的平均时间;


  1. write_size_per_second:master 平均每秒产生的命令数据量大小(写命令和数据大小总和);


例如,如果主服务器平均每秒产生 1 MB 的写数据,而从服务器断线之后平均要 5 秒才能重新连接上主服务器,那么复制积压缓冲区的大小就不能低于 5 MB。


为了安全起见,可以将复制积压缓冲区的大小设为2 * second * write_size_per_second,这样可以保证绝大部分断线情况都能用部分重同步来处理。


相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
相关文章
|
5天前
|
SQL Java 数据库连接
Mybatis架构原理和机制,图文详解版,超详细!
MyBatis 是 Java 生态中非常著名的一款 ORM 框架,在一线互联网大厂中应用广泛,Mybatis已经成为了一个必会框架。本文详细解析了MyBatis的架构原理与机制,帮助读者全面提升对MyBatis的理解和应用能力。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
Mybatis架构原理和机制,图文详解版,超详细!
|
19天前
|
开发者 容器
Flutter&鸿蒙next 布局架构原理详解
本文详细介绍了 Flutter 中的主要布局方式,包括 Row、Column、Stack、Container、ListView 和 GridView 等布局组件的架构原理及使用场景。通过了解这些布局 Widget 的基本概念、关键属性和布局原理,开发者可以更高效地构建复杂的用户界面。此外,文章还提供了布局优化技巧,帮助提升应用性能。
78 4
|
19天前
|
存储 Dart 前端开发
flutter鸿蒙版本mvvm架构思想原理
在Flutter中实现MVVM架构,旨在将UI与业务逻辑分离,提升代码可维护性和可读性。本文介绍了MVVM的整体架构,包括Model、View和ViewModel的职责,以及各文件的详细实现。通过`main.dart`、`CounterViewModel.dart`、`MyHomePage.dart`和`Model.dart`的具体代码,展示了如何使用Provider进行状态管理,实现数据绑定和响应式设计。MVVM架构的分离关注点、数据绑定和可维护性特点,使得开发更加高效和整洁。
146 3
|
1月前
|
容器
Flutter&鸿蒙next 布局架构原理详解
Flutter&鸿蒙next 布局架构原理详解
|
3月前
|
SQL DataWorks 关系型数据库
DataWorks操作报错合集之如何处理数据同步时(mysql->hive)报:Render instance failed
DataWorks是阿里云提供的一站式大数据开发与治理平台,支持数据集成、数据开发、数据服务、数据质量管理、数据安全管理等全流程数据处理。在使用DataWorks过程中,可能会遇到各种操作报错。以下是一些常见的报错情况及其可能的原因和解决方法。
|
1月前
|
监控 关系型数据库 MySQL
深入了解MySQL主从复制:构建高效稳定的数据同步架构
深入了解MySQL主从复制:构建高效稳定的数据同步架构
120 1
|
2月前
|
canal 消息中间件 关系型数据库
Canal作为一款高效、可靠的数据同步工具,凭借其基于MySQL binlog的增量同步机制,在数据同步领域展现了强大的应用价值
【9月更文挑战第1天】Canal作为一款高效、可靠的数据同步工具,凭借其基于MySQL binlog的增量同步机制,在数据同步领域展现了强大的应用价值
633 4
|
3月前
|
关系型数据库 MySQL 数据库
【MySQL】手把手教你MySQL数据同步
【MySQL】手把手教你MySQL数据同步
|
1月前
|
消息中间件 NoSQL 关系型数据库
一文彻底搞定Redis与MySQL的数据同步
【10月更文挑战第21天】本文介绍了 Redis 与 MySQL 数据同步的原因及实现方式。同步的主要目的是为了优化性能和保持数据一致性。实现方式包括基于数据库触发器、应用层双写和使用消息队列。每种方式都有其优缺点,需根据具体场景选择合适的方法。此外,文章还强调了数据同步时需要注意的数据一致性、性能优化和异常处理等问题。
350 0
|
3月前
|
SQL 关系型数据库 MySQL
“震撼揭秘!Flink CDC如何轻松实现SQL Server到MySQL的实时数据同步?一招在手,数据无忧!”
【8月更文挑战第7天】随着大数据技术的发展,实时数据同步变得至关重要。Apache Flink作为高性能流处理框架,在实时数据处理领域扮演着核心角色。Flink CDC(Change Data Capture)组件的加入,使得数据同步更为高效。本文介绍如何使用Flink CDC实现从SQL Server到MySQL的实时数据同步,并提供示例代码。首先确保SQL Server启用了CDC功能,接着在Flink环境中引入相关连接器。通过定义源表与目标表,并执行简单的`INSERT INTO SELECT`语句,即可完成数据同步。
338 1