专栏首页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 条评论
登录 后参与评论

相关文章

  • MySQL半同步复制

    MySQL通过复制(Replication)实现存储系统的高可用。目前,MySQL支持的复制方式有:

    linjinhe
  • C++字符串处理小结

    常用的C++的字符串类型主要是std::string。它是模板std::basic_string的一个实例化。另外还有三个实例化std::wstring、std...

    linjinhe
  • WiscKey:LSM-Tree 写放大优化WiscKey 简介WiscKey 带来的好处WiscKey 面临的问题和挑战参考文档

    WiscKey 的提出,主要是为了优化 LSM-Tree 的写放大问题。此前已经有不少论文讨论过这个问题,如 LSM-trie 和 PebblesDB,但是大部...

    linjinhe
  • 高可用的本质: 复制

    服务和数据的高可用性本质上是靠“复制”来解决的,比如服务通过集群部署多台机器来完成,数据通过冗余的多副本机制来完成。对于服务来说,只需要部署多个实例即可,特别是...

    luoxn28
  • 全局复制:允许你复制任何应用内文字

    美丽应用
  • MYSQL 多源复制,过滤复制与应用场景

    Mysql的使用中,会伴随着一个其他数据库中很少被提到的问题,数据融合。ORACLE ,SQL SERVER ,PG 你可以去分区表,MYSQL 中遇到这样的问...

    AustinDatabases
  • PostgreSQL 逻辑复制学习中的深入与疑问

    首先逻辑复制早期在 PG 10 之前是通过插件的方式来实现其功能的,在PG10合并进数据库系统中。

    AustinDatabases
  • 人工智能创新有望解决大数据难题

    导读:数据科学界经常开玩笑说,专家系统好比是过时的恐龙,它们很有意思,但是就现代应用而言不切实际。我完全不同意,人工智能领域没有哪一项进步完全取代得了专家系统的...

    钱塘数据
  • 老板丢给我60万行的Excel数据,幸亏我会Python,不然就惨了

    一个朋友在某运动品牌公司上班,老板给他布置了一个处理客户订单数据的任务。要求是根据订单时间和客户id判断生成四个新的数据:

    诸葛青云
  • 老板丢给我60万行的Excel数据,幸亏我会Python,不然就惨了

    一个朋友在某运动品牌公司上班,老板给他布置了一个处理客户订单数据的任务。要求是根据订单时间和客户id判断生成四个新的数据:

    诸葛青云

扫码关注云+社区

领取腾讯云代金券