专栏首页linjinhe的专栏设计数据密集型应用(5):复制

设计数据密集型应用(5):复制

数据复制是每一个存储系统的重要组成部分。

数据复制带来的好处:

  1. 可用性。当某个副本不可用时,可以将请求调度到其他副本。
  2. 读扩展。在某些场景,可以将读请求分散到多个副本,以分散读压力。
  3. 降低延迟。跨地域复制,异地用户可以实现就近接入。

数据复制带来的问题:

  1. 数据一致性问题。

常见的数据复制架构:

  • 单主复制(Single-Leader Replication)
  • 多主复制(Multi-Leader Replication)
  • 无主复制(Leaderless Replication)

单主复制

传统的单主复制有:异步复制、半同步复制

这里主要参考 MySQL 的 Primary-Secondary Replication

首先,选择一台机器作为 leader,所有写请求都由 leader 执行,生成日志。

async-replication

异步复制:leader 直接提交(commit)请求,返回结果给客户端,通过后台线程将日志异步发送给 followers。

semisync-replication

半同步复制:leader 等待日志成功发送给 followers 后,提交(commit)请求,返回结果给客户端。

异步复制和半同步复制的优缺点

  • 异步复制
    • Leader 不用等日志成功发送给 followers,可用性、延迟不受 followers 的影响。
    • Followers 的数据可能落后 leader 非常多,数据一致性差。
  • 半同步复制
    • 正常情况下,leader 和 followers 之间的数据是一致的。
    • 但是每次写请求 leader 都要等待 followers 的返回,增加了延迟。
    • 同时,followers 故障也会影响 leader 的可用性。 为了避免 follwers 故障导致 leader 不可用,半同步复制一般会设置一个超时时间,如果超时了,就退化成异步复制。 或者当 followers 不止一个时,可以通过设置半同步等待 follower 返回个数来缓解这个问题。比如如果有两个 followers,当半同步复制收到其中一个 follower 的返回时,就可以提交(commit)请求。

故障转移(failover)

一般情况下,如果 followers 故障,启动后继续复制就好,不需要特殊处理。

Leader 故障的 failover 比较麻烦,总结起来有下面四件事要做:

  1. 故障检测。一般通过定时发送心跳包来实现故障检测。如果太久(比如 10s)没收到心跳包,则认为对方故障——在分布式系统中,其实很难确定一台机器是否真的故障,因为也有可能是网络问题。
  2. 提升 leader。检测到 leader 故障之后,需要选择一个 follower 将其提升为新的 leader。(这里可能还需要让旧的 leader 失效。)
  3. 将 client 的请求路由到新的 leader。
  4. 让其它 followers 切换到新的 leader。

细究起来,failover 这里有很多细节需要特别注意,一不留神就会留下 bug,比如:

  1. 如果使用异步复制,新 leader 的数据可能不是最新的,是否可以接受?旧 leader 重启后要如何处理不一致的数据?
  2. 脑裂(split brain):如何避免同时存在两个 leader?

Paxos/Raft 可以实现强一致的单主复制和故障转移,同时解决脑裂等问题。 关于 Paxos/Raft,可以参考链接的内容。

多主复制

多主复制,也称为 Multi-Master Replication

从数据库的角度看,多主复制可以实现写扩展和就近接入降低延迟,但是存在写冲突的问题 —— 如果同一条记录在不同的 master 被同时修改,就产生了冲突。

还有一些业务场景符合多主复制的逻辑,不过这里多主复制不一定是为了提升写性能。比如:

  1. App 的多端数据同步。每一个终端就是一个 master。CouchDB 是一个专门为解决这类问题设计的数据库。
  2. 多人协作编辑,比如腾讯文档、Google Docs。这里每一个人就是一个 master。

冲突处理

冲突处理最简单的方式就是避免冲突——让每个 master 修改一个独立的数据集合。

如果 masters 修改的数据集合会有相交,就有可能出现冲突。

在进行冲突处理之前要先检测出冲突的数据,一般可以通过 vector clock 来维护数据修改的时序依赖,以此来检测不同 master 修改是否有冲突。

冲突处理则要根据应用的容忍性进行选择,比如选择时间戳最大的、选择某个 master 的修改、业务定制化处理等等。

个人觉得,如果是为了实现写性能的扩展,可以通过分片(sharding)来实现。 实际业务中,建议避免使用这类会产生冲突的多主复制。 通过 Paxos 也可以实现强一致的多主复制,可以参考 MySQL Group Replication

无主复制

TooManyPigeons

无主复制,其实就是 NRW,复制逻辑是由客户端驱动的。 NRW 的思路来自鸽巢原理

N - 复制的副本数。

R - 每次读操作需要读的副本数。

W - 每次写操作需要写的副本数。

一般情况下:

  1. 为了确保多数派故障数据不丢:W >= (N/2) + 1。
  2. 为了能读取到最新数据:R + W > N。
  3. 为了不受单机故障的影响:W < N && R < N。

比如,N 为 3。

比较稳妥的选择是:W=2,R=2。

为了最求写性能,W=1,R=3,这样单机故障有丢失数据的风险。

为了最求读性能,W=3,R=1,这样单机故障写操作就会失败。

NRW 看起来可以从理论上保证每次读取到最新的数据,但是实际上没那么简单:

  1. 由于复制是客户端驱动的——客户端直接向多个 servers 发起写请求,多个客户端的并发操作如何处理?如何保证多个结点数据的一致性?
  2. 脏读:读写并发,读到的数据可能还没完成复制。
  3. 脏读:部分写失败也可能被读到。

想深入了解 NRW 的话,可以看论文 Dynamo: amazon's highly available key-value store

小结

  1. 个人认为,半同步复制已经可以抛弃了。因为:
    • 延迟上,半同步复制相比 Paxos/Raft 没有优势,都是一个 RTT。
    • 一致性上,Paxos/Raft 保证了数据的一致性,自动 failover 和处理脑裂问题的机制更加完整、完善。半同步复制的自动 failover 不完善,而且难以解决脑裂问题。
  2. 不建议使用需要业务或外部进行冲突处理的多主复制,这会让业务逻辑或系统维护变复杂。
  3. 一些特殊的简单场景可以使用 NRW,需要自己做好权衡。
  4. 异步复制在一些写入数据量大,对数据一致性要求不高的场景还有用武之地。
  5. 如果要求高可用+强一致,请选用 Paxos/Raft 作为数据复制的算法。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 设计数据密集型应用(1-2)

    假期宅家,这两天在看一本书:Designing Data-Intensive Application,书名翻译成中文是设计数据密集型应用 —— 大部分互联网应用...

    linjinhe
  • 设计数据密集型应用-Data-Intensive Application

    读一本好书《设计数据密集型应用》- Designing Data-Intensive Application

    王炸
  • 设计数据密集型应用(3):Storage and Retrieval

    Hash Index 是一种相对简单的索引结构。几乎每一种程序设计语言都有提供内存数据结构 hash map/table 的标准库,比如 C++ 中的 std:...

    linjinhe
  • 设计数据密集型应用(4):Encoding and Evolution

    分布式系统滚动升级的过程中,新旧数据与代码是同时并存的。如果出现异常,可能还需要回退程序。因此,升级过程中需要保证:

    linjinhe
  • 设计数据密集型应用(6-7):分片、事务

    随着业务发展,用户数量、商品数量、订单数量都在持续增长,数据库的负载越来越高。我们开始对数据库进行垂直拆分(垂直分片),把这三张表拆到三个数据库,而业务代码改改...

    linjinhe
  • 《数据密集型应用系统设计》读书笔记(一)

    目前许多的新型应用都属于「数据密集型」(data-intensive),而不是计算密集型(compute-intensive),对于这些应用,CPU 的处理能力...

    口仆
  • 《数据密集型应用系统设计》读书笔记(二)

    「数据模型」(Data models)是软件开发中最重要的部分之一,大部分应用程序都是通过数据模型的层层叠加来构建的,例如:

    口仆
  • 『数据密集型应用系统设计』读书笔记(五)

    在前面几章,我们讨论了数据系统的各个方面,但仅限于数据存储在单台机器上的情况。现在我们进入更高的层次,在接下来的几章讨论将数据库分布到多台机器的情况。

    1ess
  • 《数据密集型应用系统设计》读书笔记(三)

    上一章讨论了数据模型与查询语言,即向数据库给出数据时数据的格式以及数据查询的机制,其可以理解为从应用开发者的角度出发讨论了上述两件事情。本章将从「数据库」的角度...

    口仆
  • 『数据密集型应用系统设计』读书笔记(四)

    在大多数情况下,修改应用程序的功能也意味着需要更改其存储的数据: 可能需要使用新的字段或记录类型,或者以新方式展示现有数据。 我们在之前讨论的数据模型有不同的方...

    1ess
  • 『数据密集型应用系统设计』读书笔记(三)

    一个数据库在最基础的层次上需要完成两件事情: 当你把数据交给数据库时,它应当把数据存储起来;而后当你向数据库要数据时,它应当把数据返回给你。 上一章,我们讨论了...

    1ess
  • 『数据密集型应用系统设计』读书笔记(二)

    在本章中,我们将研究一系列用于数据存储和查询的通用数据模型。特别地,我们将比较关系模型,文档模型和少量基于图形的数据模型。我们还将查看各种查询语言并比较它们的用...

    1ess
  • 『数据密集型应用系统设计』读书笔记(一)

    这本书一直在我的待读列表,但是一直没有机会拜读,直到最近 2021 年已经快要过去,感觉需要在年末提升一下自己。边读边做一下笔记,留待后用。

    1ess
  • 设计数据密集型应用(8-9):从单机到分布式

    所以,当你通过网络发送一个数据包的时候,程序必须考虑到这个数据包可能丢失、也可能延迟。

    linjinhe
  • 数据密集型系统架构设计

    按照使用的资源类型划分,我们可以把系统分为三大类型:IO密集型、计算密集型,数据密集型。系统的类型反映了系统的主要瓶颈。现实情况中,大部分系统在由小变大的过程中...

    CSDN技术头条
  • 设计数据密集型应用(10-11):大数据的批处理和流处理

    谈大数据批处理,绕不过的就是 MapReduce。MapReduce 是大数据处理的老祖宗了。

    linjinhe
  • 复杂单页应用的数据层设计

    很多人看到这个标题的时候,会产生一些怀疑: 什么是“数据层”?前端需要数据层吗? 可以说,绝大部分场景下,前端是不需要数据层的,如果业务场景出现了一些特殊的需求...

    前朝楚水
  • Unity应用架构设计(6)——设计动态数据集合ObservableList

    什么是 『动态数据集合』 ?简而言之,就是当集合添加、删除项目或者重置时,能提供一种通知机制,告诉UI动态更新界面。有经验的程序员脑海里迸出的第一个词就是 O...

    用户1161731
  • TDSQL 在微信支付数据密集型应用落地实践

    日前,腾讯云数据库开源产品TDSQL PG版(开源代号TBase)宣布推出重磅升级——经过一年半的打磨,上万张表访问场景下,内存占用节省60%;查询性能提升百...

    腾讯云数据库 TencentDB

扫码关注云+社区

领取腾讯云代金券