【设计数据密集型应用】复制

  • 👏作者简介:大家好,我是爱敲代码的小黄,阿里淘天Java开发工程师,CSDN博客专家
  • 📕系列专栏:Spring源码、Netty源码、Kafka源码、JUC源码、dubbo源码系列
  • 🔥如果感觉博主的文章还不错的话,请👍三连支持👍一下博主哦
  • 🍂博主正在努力完成2023计划中:以梦为马,扬帆起航,2023追梦人
  • 📝联系方式:smallyellow521,加我进群,大家一起学习,一起进步,一起对抗互联网寒冬👀

文章目录

  • 复制
    • 领导者与追随者
    • 同步复制与异步复制
    • 如何设置新从库
    • 处理节点宕机
      • 从库失效:追赶恢复
      • 主库失效:故障切换
    • 复制日志的实现
      • 基于语句的复制
      • 逻辑日志复制(基于行)
      • 基于触发器的复制
  • 复制延迟问题
      • 读已之写
      • 单调读
      • 一致前缀读
      • 多主复制
        • 运维多个数据中心
        • 需要离线操作的客户端
        • 协同编辑
      • 处理写入冲突
        • 同步与异步冲突检测
        • 避免冲突
        • 收敛至一致的状态
      • 多主复制拓扑
    • 无主复制
      • 节点故障
        • 读修复和反熵
  • 总结

与可能出错的东西比,“不可能”出错的东西最显著的特点就是:一旦真的出错,通常就彻底玩完了。

—— 道格拉斯・亚当斯(1992)

复制

简介:通过网络连接的多台机器上保留相同数据的副本。

原因:降低延时、可用性、吞吐量

  • 数据存放地与用户接近,减少网络请求延时
  • 避免单机故障,提升可用性
  • 机器数量可伸缩,提升读取吞吐量

困难:

  • 如果复制的数据不随时间而改变,复制将变的非常简单
  • 但复制的过程中,数据经常是变更的

解决:

在分布式场景下,一般使用三种变更复制算法:

  • 单领导者
  • 多领导者
  • 无领导者

领导者与追随者

副本:存储数据库拷贝的每个节点

问题:当存在多个副本时,如何确保所有数据都落在了所有的副本上?

解决方案:基于领导者的复制

原理:

image-20240219231709711

  • 我们将其中一个副本指定为 领导者(主库)
    • 当客户端想要写入数据时,必须将请求发送给领导者(主库)
    • 它将数据写入到本地存储
  • 其他副本成为追随者(只读副本)
    • 每当领导者将新数据写入本地存储时,它也会将数据变更发送给所有的追随者,称为复制日志。
    • 每个追随者从领导者拉取日志,并更新本地数据库副本,按照领导者相同的处理顺序来进行写入
  • 当客户想要从数据库查询数据
    • 领导者和任一追随者都可以查询
    • 只有领导者可以写入数据

同步复制与异步复制

image-20240219233053467

  • Follower 1:同步复制
    • 好处:保持与主库强一致的最新数据
    • 坏处:从库挂掉,主库也无法写入数据
  • Follower 2:异步复制
    • 好处:从库挂掉,主库也能写入数据
    • 坏处:无法保持与主库强一致的最新数据

目前业界常用方式:半同步

  • 在数据库中开启同步复制,其中一个从库是同步的,其余所有的从库均是异步的
  • 如果该从库不可用,则将另外一个异步从库改为同步复制

通常情况下,基于领导者的复制都配置成完全异步,一旦主库失效不可恢复,复制给从库的数据也将丢失。

即使已经向客户端确定成功,写入也不能保证是持久的。

当然也有优点:即使所有的从库都落后了,主库也可以正常写入。

如何设置新从库

原因:

  • 增加从库副本数量
  • 替换失败的节点

过程:

  • 在某个时刻获取主库的一致性快照
  • 将快照复制到新的从库节点
  • 从库链接主库并拉取快照后的所有数据变更
  • 等从库处理完快照之后的数据变更,从库就赶上了主库

复制过程可参考:RedisMySQL

处理节点宕机

系统中的任何节点都可能宕机,即使个别节点生效,也能保持整个系统的运行并尽可能控制节点停机带来的影响

从库失效:追赶恢复

在本地磁盘中,从库记录从主库收到的数据变更。

如果从库崩溃并重新启动或者网络中断等原因,从本地日志中获取最后一个事务

连接到主库,请求在从库断开期间发生的所有数据变更,当解决完这些变更之后,就赶上了主库,正常接受数据变更流。

主库失效:故障切换

故障切换:将其中一个从库提升为新的主库,重新配置客户端,将他们的写操作发送给新的主库,其他从库需要开始拉取来自新主库的数据变更。

过程:

  • 确定主库失效(心跳检测)
  • 选择一个新的主库(选举机制)
  • 重新配置系统以启动新的主库

问题:

  • 异步复制,新主库的数据落后老主库。解决方式:丢掉老主库中未复制的写入

  • 数据库与外部存储协调,丢弃写入内容极其危险

    例如在 GitHub 的一场事故中,一个过时的 MySQL 从库被提升为主库

    数据库使用自增 ID 作为主键,因为新主库的计数器落后于老主库的计数器,所以新主库重新分配了一些已经被老主库分配掉的 ID 作为主键

    这些主键也在 Redis 中使用,主键重用使得 MySQL 和 Redis 中的数据产生不一致,最后导致一些私有数据泄漏到错误的用户手中。

  • 脑裂,两个节点都误认为自己是主库

  • 超时时间的配置,如何正确的配置主库失效的超时时间

复制日志的实现

基于语句的复制

主库记录每个写入请求并将该语句发送给从库

问题:

  • 任何调用 非确定下函数 的语句,在每个副本生成不同的值。
    • NOW():获取当前时间
    • RAND():获取一个随机数
  • 语句必须按照顺序执行,避免并发问题
    • UPDATE … WHERE <某些条件>,必须现有某些条件数据,再进行 UPDATE

### 传输预写式日志(WAL)

存储引擎通常会将写操作追加到日志中

mysql 通过 redo、undo 日志实现 WAL。

redo log 称为重做日志,每当有操作时,在数据变更之前将操作写入 redo log,这样当发生掉电之类的情况时系统可以在重启后继续操作。

undo log 称为撤销日志,当一些变更执行到一半无法完成时,可以根据撤销日志恢复到变更之间的状态。

mysql 中用 redo log 来在系统 Crash 重启之类的情况时修复数据(事务的持久性),而 undo log 来保证事务的原子性。

缺点:由于日志记录的非常底层(WAL包含哪些磁盘块中的哪些字节发生了变化),当从库和主库运行不同版本时,会出现数据解析问题。

逻辑日志复制(基于行)

复制和存储引擎使用不同的日志格式,将复制日志从存储引擎的内部实现中解耦出来,这种复制日志被称为逻辑日志。

例如:MySQLBinlog

  • 插入行:日志包含所有列的新值
  • 删除行:日志包含主键
  • 更新行:日志包含主键及更新列的新值

优点:逻辑日志和存储引擎内部实现解耦,系统可以做到兼容。从而使主库和从库可以运行不同版本的数据库软件。

基于触发器的复制

将数据更改发生时的自定义代码记录在数据库系统中,使用外部程序读取该表,进行响应的复制。

复制延迟问题

当前存在一个主库,多个从库,在数据复制的过程中,如果从库落后于主库,我们会看到过时的信息。

对主库和从库执行相同的查询,得到不同的结果,等后续一段时间后,从库追上主库保持一致,被称为 最终一致性

读已之写

image-20240220234051681

用户在界面提交一些数据,将其写入到主库,主库异步复制给从库,从而从库追赶上主库。

但如果用户在提交完数据后,立即查询(从库),会发现自己提交的数据库丢失不见

这种情况下,我们需要 写后读一致性,也称为 读己之写一致性,我们需要保证:如果用户重新加载页面,他们总会看到他们自己提交的任何更新

解决方法:

  • 对于用户可能修改的内容,总是从主库获取
    • 用户个人资料只能本人编辑,而不能其他人编辑
    • 从主库读取自己的档案,其他用户档案去从库读取
  • 跟踪上次更新的时间,在数据更新的一分钟内,从主库读取
  • 监控从库的复制延迟,滞后主库超过一分钟的从库不接受查询请求

单调读

image-20240220235435088

当前从库 2 落后从库 1如果用户从不同从库读取

  • 先读取的从库 1,拿到了最新数据
  • 后续读取从库 2,拿到了旧数据

从用户体验来看,时间看上去好像回退了,所以我们需要单调的读取。

单调读 可以保证这种异常不会发生,其程度比 强一致性 弱,比 最终一致性 更强。

实现方式:每个用户总是从一个副本中进行读取(不同客户可以从不同副本读取)。可以基于用户 ID Hash 来选择副本,而不是随机选择读取的数据库副本。

一致前缀读

如果我们有两个因果关系的数据:

Mr. Poons

​ Mrs. Cake,你能看到多远的未来?

Mrs. Cake

​ 通常约十秒钟,Mr. Poons.

假如:

  • Cake 说的话是一个延迟较低的从库
  • Poons 说的话是一个延迟较高的从库

当第三个人在读取数据时,会出现这种情况:

Mrs. Cake

​ 通常约十秒钟,Mr. Poons.

Mr. Poons

​ Mrs. Cake,你能看到多远的未来?

如果某些分区的复制速度慢于其他分区,那么观察者可能会在看到问题之前先看到答案。

需要保证一致前缀读:如果一系列写入按照某个顺序进行,那么任何人读取这些写入时,也看以同样的顺序读取。

解决方案:确保任何因果相关的写入都写入到相同的分区

多主复制

如果数据库被分区,每个分区有一个主库。

多领导配置:处理写入的每个节点都必须将该数据变更转发给其他节点

在这种情况下,每个主库同时是其他主库的从库。

### 多主复制的应用场景

运维多个数据中心

image-20240222000840409

多主配置中在每个数据中心都有主库,每个数据中心内使用常规的主从复制;

在数据中心之间,每个数据中心的主库会将自身变更同步到其他主库。

单主和多主对比:

  • 性能
    • 单主:每个写入操作必须穿过互联网,进入主库所在的数据中心,网络延时较大
    • 多主:每个写入操作在本地数据中心进行处理,与其他数据中心异步复制,网络延时较小
  • 容忍数据中心停机
    • 单主:主库所在的数据中心发生故障,切换另一从库成为主库
    • 多主:数据中心可以独立于其他数据中心继续运行

多主复制的缺点:多主复制在数据库属于改装的功能,常常存在微妙的配置缺陷。因此,多主复制被认为是危险的领域,应尽可能避免。

需要离线操作的客户端

多主复制的另一种适用场景是:应用程序在断网之后仍然需要继续工作。比如:日历应用

在这种情况下,每个设备都有一个充当主库的本地数据库,在所有的设备的日志副本之间同步。

协同编辑

实时协作编辑应用程序允许多个人同时编辑文档

当一个用户编辑文档时,所做的更改将立即应用到其本地副本并异步复制到服务器。

如果不发生编辑冲突,则应用程序必须对文档加锁,为了加速协作,尽可能将加锁的单位设置的非常小。

处理写入冲突

多主复制最大的问题:写入冲突

image-20240222002907115

假如两个人同时更改一个页面

  • 用户 1 将页面标题从 A 更改为 B
  • 用户 2 将页面标题从 A 更改为 C

当异步复制时,就会出现冲突。

同步与异步冲突检测

如果在单主数据库中,两个操作是串行的,不会发生冲突

但多主数据库中,两个分别写入不同的主库,后续异步复制,必然出现冲突问题

避免冲突

处理冲突的最简单的策略就是避免它们:如果应用程序可以确保特定记录的所有写入都通过同一个主库,那么冲突就不会发生。

例如:一个用户编辑自己数据的应用程序,确保来自特定用户的请求始终路由到同一数据中心并使用该数据中心的主库进行读写。

收敛至一致的状态

我们上述的例子中,在多个主库的情况下,我们的写入顺序是不确定的

数据库必须以一种收敛的方式解决冲突,所有副本必须在变更复制完成时收敛到一个相同的最终值。

解决方案:

  • 给每个写入分配唯一ID(时间戳、长随机数、UUID),挑选最高 ID 的写入作为胜利者
  • 给每个副本分配唯一ID,ID更高的写入具有更高的优先级
  • 将这些值链接在一起,比如:B/C
  • 将冲突显式的暴露出来,交于用户决定,比如:GIT冲突

多主复制拓扑

复制拓扑用来描述写入操作从一个节点传播到另一个节点的通信路径。

image-20240222004135809

无主复制

客户端直接写入几个副本中,另一种情况,由一个 协调者 代表客户端写入。

节点故障

无主配置中,发生节点故障,不需要故障转移

如果有三个副本,两个副本写入成功,一个副本写入失败

image-20240222005455126

当我们不可用的副本重新上线,存的是落后的数据

解决方案:当一个客户端从数据库中读取数据时,它不仅仅把它的请求发送到一个副本,而是将读请求将被并行地发送到多个节点,通过版本号来确定哪个值是最近更新的。

读修复和反熵

复制方案应确保最终将所有数据复制到每个副本。在一个不可用的节点重新联机之后,它如何赶上它错过的写入?

  • 读修复:当客户端并行读取多个节点时,检测落后节点的回应,并将最新值写会落后节点。适用于频繁读取的值
  • 反熵过程:数据存储具有后台进程,进程不断查找副本之间的数据差并进行相关的复制追齐。

总结

鲁迅先生曾说:独行难,众行易,和志同道合的人一起进步。彼此毫无保留的分享经验,才是对抗互联网寒冬的最佳选择。

其实很多时候,并不是我们不够努力,很可能就是自己努力的方向不对,如果有一个人能稍微指点你一下,你真的可能会少走几年弯路。

如果你也对 后端架构中间件源码 有兴趣,欢迎添加博主微信:smallyellow521,一起学习,一起成长

我是爱敲代码的小黄,阿里巴巴淘天集团Java开发工程师,双非二本,培训班出身

通过两年努力,成功拿下阿里、百度、美团、滴滴等大厂,想通过自己的事迹告诉大家,努力是会有收获的!

双非本两年经验,我是如何拿下阿里、百度、美团、滴滴、快手、拼多多等大厂offer的?

我们下期再见。

从清晨走过,也拥抱夜晚的星辰,人生没有捷径,你我皆平凡,你好,陌生人,一起共勉。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/727965.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Liinux——(网络)socket编程

预备知识 源IP地址和目的IP地址 在IP数据包头部中, 有两个IP地址, 分别叫做源IP地址, 和目的IP地址 认识端口号 端口号(port)是传输层协议的内容. 端口号是一个2字节16位的整数;端口号用来标识一个进程, 告诉操作系统, 当前的这个数据要交给哪个进程来处理;IP地址 端口号能…

tomcat搭建个人博客 实现动静分离

jar包相关 .war&#xff1a;WebApp打包,类zip格式文件,通常包括一个应用的所有资源,比如jsp,html,配置文件等 .jar&#xff1a;EJB类文件的打包压缩类zip格式文件&#xff0c;,包括很多的class文件, 网景公司发明 .rar&#xff1a;资源适配器类打包文件&#xff0c;目前已不常…

[数据结构]OJ用队列实现栈

225. 用队列实现栈 - 力扣&#xff08;LeetCode&#xff09; 官方题解&#xff1a;https://leetcode.cn/problems/implement-stack-using-queues/solutions/432204/yong-dui-lie-shi-xian-zhan-by-leetcode-solution/ 首先我们要知道 栈是一种后进先出的数据结构&#xff0c…

艺术与科技的结合,AI绘画图生图怎么样?

AI绘画图生图是指通过人工智能技术生成的具有艺术价值的图像。它可以根据用户提供的参考图像或描述&#xff0c;自动生成具有艺术风格的新图像。这些图像可以是风景、人物、抽象画等各种形式。那么ai绘画图生图到底怎么样&#xff1f; AI绘画图生图的优点在于它可以快速、高效地…

基于springboot+vue的高校教师电子名片系统

博主主页&#xff1a;猫头鹰源码 博主简介&#xff1a;Java领域优质创作者、CSDN博客专家、阿里云专家博主、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战&#xff0c;欢迎高校老师\讲师\同行交流合作 ​主要内容&#xff1a;毕业设计(Javaweb项目|小程序|Pyt…

ANTLR4规则解析生成器(三):遍历语法分析树

文章目录 1 词法分析2 语法分析3 遍历语法分析树3.1 Listener3.2 Visitor 4 总结 1 词法分析 词法分析就是对给定的字符串进行分割&#xff0c;提取出其中的单词。 在antlr4中&#xff0c;词法规则的名称的首字母需要大写&#xff0c;右侧必须是终结符&#xff0c;通常将词法…

力扣经典题目解析--反转链表

原题地址: . - 力扣&#xff08;LeetCode&#xff09; 给你单链表的头节点 head &#xff0c;请你反转链表&#xff0c;并返回反转后的链表。 示例 1&#xff1a; 输入&#xff1a;head [1,2,3,4,5] 输出&#xff1a;[5,4,3,2,1] 题目解析 链表&#xff08;Linked List&…

仓储管理系统(WMS) 的研发历程-PRD撰写

题外话&#xff1a;PRD的展现形式有多种&#xff0c;有的人喜欢在axure上直接做产品描述&#xff0c;觉得word较为过时&#xff0c;有的人认为axure不专业&#xff0c;任何展现形式都无可厚非&#xff0c;重要的达到PRD的目的&#xff0c;PRD的目标是让团队知道需求实现细节&am…

启动Docker镜像时候,ENTRYPOINT 和CMD这两者指令的写法有什么不同和区别?

ENTRYPOINT和CMD在Dockerfile中都用于指定容器启动时执行的命令&#xff0c;但它们之间存在一些关键的区别和不同的用途&#xff1a; 1. 基本用途和行为差异 ENTRYPOINT 定义了容器启动时执行的基础命令&#xff0c;使得容器像一个可执行程序。ENTRYPOINT让你能够指定容器启动…

经验分享:水牛社怎么做?

本人也就是通过他慢慢学习成长起来的。还是一个网友推荐的&#xff0c;现在他对我来说算是大佬了&#xff0c;已经单飞了&#xff0c;好久都没有联系了&#xff0c;呵呵&#xff0c;真是人往高处走&#xff0c;水往低处流啊。 做网赚会经常和一些网络小白聊天&#xff0c;聊着…

回调函数、回调地狱、解放方法Promise的用法

回调函数 回调函数的定义非常简单&#xff1a;一个函数被当做一个实参传入到另一个函数(外部函数)&#xff0c;并且这个函数在外部函数内被调用&#xff0c;用来完成某些任务的函数。就称为回调函数回调函数的两种写法(实现效果相同)&#xff1a; const text () > {docum…

个人项目介绍4:三维园区篇

个人项目介绍: 地图铁路线路篇 地球卫星篇 火车站篇 三维园区篇 项目需求&#xff1a; 1.按比例全景显示三维园区 2.精确显示园区内设备设施 3.实时显示设备报警信息 4.显示园区内摄像监控设备&#xff0c;并可点击显示监控视频流 5.显示园区内的重大危险源和风险分布 …

PCL 路面点云标线提取(C++详细过程版)

目录 一、算法原理二、代码实现三、结果展示本文由CSDN点云侠原创,原文链接。如果你不是在点云侠的博客中看到该文章,那么此处便是不要脸的爬虫与GPT。 一、算法原理 算法来自本人自创。实现效果如下图所示,具体实现原理看代码即可。 二、代码实现 #include

bean的管理方式

默认情况下&#xff0c;spring项目启动时&#xff0c;会把bean对象全部创建好放到ioc容器 主动获取bean对象&#xff1a;getBean里面传入bean的名称或bean的类型 注意如果没有主动设置bean的名称&#xff0c;则默认名称是对应类名的首字母小写 在ioc容器中&#xff0c;bean对…

今天分享一个好看的输入法皮肤相信每个人心里住着一个少女心我们美化一下她吧

标题&#xff1a; 白日梦皮肤上线&#xff0c;百度输入法助你开启梦幻之旅&#xff01; 正文&#xff1a; 大家好呀&#xff01;今天我来给大家安利一款超级梦幻的百度输入法皮肤——“白日梦”系列&#xff01; 这款皮肤的设计灵感来源于我们内心深处的白日梦&#xff0c;充…

14. C++继承与虚函数

【继承基础概念】 继承可以让本类使用另一个类的非私有成员&#xff0c;提供共用成员的类称为父类或基类&#xff0c;使用共用成员的类称为子类或派生类&#xff0c;子类创建对象时会包含继承自父类的成员。 继承的优势是减少重复定义数据&#xff0c;当本类需要在另一个类的…

L1-009 N个数求和

MD...提交过了好几次才通过。 第三个测试点: 需要使用long long&#xff0c;要求长整型。干脆就把int全部替换成long long。 第五个测试点: 随便试出来的&#xff0c;我输入了2 1/2 -1/2,发现啥都没打印出来。原来是忽略了结果是0的情况&#xff0c;如果整数部分和分子部分都…

LabVIEW高温摩擦磨损测试系统

LabVIEW高温摩擦磨损测试系统 介绍了一个基于LabVIEW的高温摩擦磨损测试系统的软件开发项目。该系统实现高温条件下材料摩擦磨损特性的自动化测试&#xff0c;通过精确控制和数据采集&#xff0c;为材料性能研究提供重要数据支持。 项目背景 随着材料科学的发展&#xff0c;…

git revert 撤回之前的几个指定的提交

文章目录 Intro操作命令-n 选项 参考 Intro 在开发过程中&#xff0c;有的时候一开始只是一个小需求&#xff0c;可以改着改着事情超出了控制&#xff0c;比如说我一开始只是想调整一个依赖包的版本&#xff0c;可是改到后来类库不兼容甚至导致项目无法启动。 这个时候我就想&…

npm市场发布包步骤

1.打开npm官网npm官网 2.创建自己的账号 3.查看当前npm的镜像源&#xff0c; 如果出现淘宝的镜像源则需要切换成官方的镜像源 npm config get registry //查看镜像源 https://registry.npm.taobao.org/ //淘宝的镜像源 https://registry.npmjs.org/ //官方的镜像源 …