字节跳动Redis变种Abase:无主多写架构如何解决高可用难题

发布于:2025-09-13 ⋅ 阅读:(24) ⋅ 点赞:(0)

参考:

https://zhuanlan.zhihu.com/p/614227806

https://www.51cto.com/article/709845.html

Abase的核心突破在于通过​​无主多写架构​​结合​​混合逻辑时钟​​,从根本上解决了传统主从架构在高可用性上的瓶颈(慢节点、切换延迟),同时通过灵活的配置、多租户和高效的同步合并机制,实现了在大规模、多业务场景下对性能、成本、一致性和可用性的完美平衡。

Abase解决了什么问题?

Abase是字节跳动为解决其大规模在线业务(如推荐、电商、广告等)的底层数据存储需求而开发的系统。它核心解决了Redis的以下几个关键问题:

  1. ​极致高可用性挑战​​:

    • ​传统主从架构的固有缺陷​​:主节点故障后,秒级的切换时间会导致服务不可用,无法满足字节业务对毫秒级延迟和极高可用性的要求。
    • ​慢节点问题​​:主从架构中,写入是单点。若主节点未完全宕机但性能下降(如CPU、磁盘、网络问题),成为“慢节点”,会直接拖累所有写入请求,且难以通过简单阈值进行自动处理,频繁切主又会带来新问题。
    • ​极端故障恢复慢​​:在跨机房部署(3AZ)下,遇到整个机房故障等极端情况,传统方案需要人工介入决策和切换,耗时长达数分钟到半小时,期间服务受影响。
  2. ​大规模下的成本与效率问题​​:

    • ​海量数据与吞吐​​:需要支撑百亿级QPS、百PB级数据量,对系统的扩展性、吞吐量和容量提出了极高要求。
    • ​资源隔离与成本控制​​:多个业务共享集群时,需要防止个别用户的异常流量(IO过高)影响整个集群的稳定性,并有效降低总体硬件成本。
    • ​兼容性与易用性​​:需要兼容Redis协议及生态(如Hive数据导入),降低业务迁移和使用的成本。
  3. ​多活架构下的数据一致性问题​​:

    • 传统的跨集群多活方案通过中间件同步数据,难以保证数据的最终一致性,冲突解决复杂。

Abase是如何解决的?

Abase 2.0的核心设计通过一套​​创新的“无主多写”架构​​来解决上述问题:

  1. ​采用无主多写架构实现极致高可用​​:

    • ​原理​​:摒弃单一主节点,任何副本(Replica)都可以接受写入请求。写入请求由协调器(Coordinator)分配一个​​全局唯一的混合逻辑时钟(HLC)时间戳​​,然后并发写入多个副本。
    • ​解决慢节点与切换延迟​​:写入时,只需等待配置数量的副本(Quorum,如2/3)成功即可返回客户端成功。如果某个副本响应慢,请求可以快速路由到其他副本,从架构上规避了单点慢节点的影响,消除了主从切换带来的秒级不可用时间。
  2. ​智能的数据冲突解决与同步机制​​:

    • ​冲突解决​​:采用 ​​“最后写入获胜”(Last Write Win)​​ 策略,依靠全局唯一的HLC时间戳来判断数据的先后顺序,自动解决不同副本间的写入冲突。
    • ​高效同步​​:借鉴有主架构的优点,在网络正常时,副本间会保持一个递增的日志流进行同步,减少数据差异比对(Diff)的开销。同时定期进行数据合并(Compaction),将多版本数据合并为单版本,提升查询性能。
  3. ​灵活的配置策略满足多样业务需求​​:

    • ​模式可配​​:支持​​无主多写模式​​(极致可用性)和​​单主模式​​(更强一致性),用户可根据业务特点选择。
    • ​Quorum可配​​:用户可设置写入成功的副本数(W)和读取所需的副本数(R)。例如,缓存场景可设W=1以追求极致性能,金融场景可设W=3以追求强一致性。
  4. ​多租户与资源隔离以优化成本与稳定性​​:

    • 通过多租户架构将不同用户的IO请求进行聚合和隔离,并使用资源池限制每个用户的资源使用量,防止个别用户影响整个集群,实现了更好的负载均衡,降低了整体硬件成本。
  5. ​原生支持异地多活​​:

    • 一个Abase集群即可跨多个地域(Region)部署,提供真正的异地多活能力,无需依赖外部中间件同步数据,并通过HLC时间戳机制保障跨地域数据的一致性。
  6. ​性能优化​​:

    • ​协程模型​​:采用协程替代纯异步框架,让请求在单线程内尽量完成(RunToComplete),减少线程切换开销。
    • ​双层存储引擎​​:针对多写产生的多版本数据,使用内存索引和Log存储未合并的数据,使用KV引擎(RocksDB)存储已合并的单版本数据,兼顾了写入性能和查询效率。

Hybrid Logical Clock (HLC) 如何保证时间戳唯一

HLC(混合逻辑时钟)是一种结合物理时钟和逻辑时钟的机制,用于在分布式系统中生成全局唯一且可比较的时间戳,解决多写冲突时的数据版本问题。其核心设计如下:


​HLC 的生成规则​

  1. ​物理时钟部分(Physical Time, PT)​​:

    • 使用机器的本地物理时间(如 Unix 时间戳),但允许不同机器间存在时钟漂移(不强制完全同步)。
    • 物理时间提供时间戳的“基础部分”,确保时间戳与真实时间大致相关。
  2. ​逻辑时钟部分(Logical Clock, LC)​​:

    • 每个节点维护一个本地计数器(LC),初始为 0。
    • 每次本地事件(如写入操作)发生时,LC 递增。
    • 当节点收到外部消息(如其他副本的写入请求)时:
      • 比较消息中的 HLC(PT_remote, LC_remote)和本地 HLC(PT_local, LC_local)。
      • PT_remote > PT_local,则更新本地 PT 为 PT_remote,并重置 LC 为 LC_remote + 1
      • PT_remote == PT_local,则取 max(LC_remote, LC_local) + 1 作为新 LC。
      • PT_remote < PT_local,则仅递增本地 LC。
  3. ​唯一性保障​​:

    • 若两个 HLC 的物理时间不同(PT1 ≠ PT2),则直接按 PT 大小比较。
    • 若物理时间相同(PT1 = PT2),则通过逻辑时钟(LC)区分,且 LC 严格递增。
    • ​额外措施​​:在 HLC 中嵌入节点 ID(如机器 IP 或副本编号),即使 PT 和 LC 完全冲突(极端罕见),节点 ID 也能确保全局唯一。

​为什么 HLC 优于纯物理时钟或纯逻辑时钟?​

  • ​物理时钟问题​​:NTP 同步误差可能导致不同机器的时间戳冲突(如时钟回拨)。
  • ​逻辑时钟问题​​(如 Lamport Clock):无法反映真实时间顺序,且依赖全局通信。
  • ​HLC 优势​​:
    • 既保留物理时间的直观性(近似真实时间顺序),又通过逻辑时钟解决冲突。
    • 无需严格时钟同步(容忍一定漂移),适合分布式环境。

 

​Anti-Entropy(反熵)机制详解​

Anti-Entropy 是 Abase2 用来​​检测和修复副本间数据差异​​的核心机制,确保多主(Multi-Leader)架构下数据最终一致。它的核心目标是:​​快速发现副本间的数据不一致,并高效同步缺失或冲突的数据​​。


为什么需要 Anti-Entropy?​

在多主模式下,写入可能仅需部分副本(W < N)确认即可返回成功,因此副本间可能存在短暂的不一致。Anti-Entropy 负责:

  • ​检测不一致​​:找出哪些副本缺少某些数据。
  • ​修复不一致​​:从最新副本拉取缺失数据,使所有副本最终一致。

vs. Merkle Tree​

传统分布式系统(如 DynamoDB、Cassandra)通常使用 ​​Merkle Tree​​ 来检测数据差异:

  • ​Merkle Tree 原理​​:通过哈希树结构(每个叶子节点是数据块的哈希,非叶子节点是子节点哈希的合并)快速比对数据差异。
  • ​缺点​​:
    • 需要扫描整个数据集,计算量大,​​资源消耗高​​。
    • 数据量大时,​​延迟高​​(可能需要分钟级才能完成检测)。

​Abase2 的优化​​:

  • 采用 ​​ReplicaLog + Seqno​​ 机制,避免全量扫描,实现​​秒级一致性检测​​。

ReplicaLog 和 Seqno 的作用​

​(1) ReplicaLog(副本日志)​

  • 每个副本(Replica)维护自己的 ​​ReplicaLog​​,记录所有写入操作。
  • 每个写入操作会被分配一个 ​​Seqno​​(严格递增的日志序列号),用于唯一标识操作顺序。
  • ​ReplicaLog 存储在内存​​(正常情况下),极端情况下(如网络分区)可持久化到磁盘。

​(2) Seqno(序列号)​

  • 每个副本的 ReplicaLog 都有一个 ​​Seqno 向量​​(例如 [Seqno_A, Seqno_B, Seqno_C]),表示该副本已经处理了哪些 ReplicaLog 的操作。
  • ​示例​​:
    • Replica A 的 Seqno 向量:[100, 95, 80](表示它已经处理了 Replica A 的 100 条日志、Replica B 的 95 条日志、Replica C 的 80 条日志)。
    • Replica B 的 Seqno 向量:[100, 100, 70](表示它已经处理了 Replica A 的 100 条日志、Replica B 的 100 条日志、Replica C 的 70 条日志)。
    • ​对比发现​​:Replica B 比 Replica A 少了 Replica C 的 80-70=10 条日志,因此需要同步这 10 条日志。

​(3) Anti-Entropy 的工作流程​

  1. ​获取 Seqno 向量​​:
    • 每个副本定期(如每秒)向其他副本发送自己的 Seqno 向量。
  2. ​比较 Seqno 向量​​:
    • 如果发现某个 ReplicaLog 的 Seqno 落后(如 Replica B 的 Replica C 日志比 Replica A 少 10 条),则进入同步流程。
  3. ​拉取缺失数据​​:
    • 从领先的副本(如 Replica A)拉取 Replica C 的 Seqno 71-80 的日志,应用到本地。
  4. ​数据合并 & GC​​:
    • 当所有副本在某 ReplicaLog 的 Seqno 前达成一致(如都确认 Replica C 的 Seqno 80 之前的日志已同步),则可以回收 Seqno 80 之前的日志(减少内存占用)。

对比:ReplicaLog+Seqno vs. Merkle Tree​

​机制​ ​ReplicaLog + Seqno(Abase2)​ ​Merkle Tree(传统方案)​
​检测方式​ 比较 Seqno 向量(内存操作) 扫描数据计算哈希树(IO 密集型)
​延迟​ 秒级(甚至毫秒级) 分钟级(大数据量时更慢)
​资源消耗​ 低(仅维护 Seqno 向量) 高(需计算和存储 Merkle Tree)
​适用场景​ 高频写入、低延迟修复 数据量小、一致性要求不高的场景

​Abase2 的优势​​:

  • ​高效​​:仅需比较 Seqno 向量,无需扫描数据,检测和修复速度极快。
  • ​低开销​​:ReplicaLog 主要存储在内存,仅极端情况下才持久化。
  • ​实时性​​:可做到秒级数据同步,保证最终一致的延迟极低。

示例场景​

假设一个 Abase2 集群有 3 个副本(A、B、C),采用 W=2(写入成功需 2 副本确认):

  1. ​写入流程​​:

    • 用户写入数据 X,Proxy 将请求发给 Replica A。
    • Replica A 记录到自己的 ReplicaLog(Seqno_A++),并转发给 Replica B 和 C。
    • Replica B 确认写入成功(Seqno_A++),但 Replica C 因网络问题未收到请求。
    • 此时:
      • Replica A 的 Seqno 向量:[Seqno_A=100, Seqno_B=95, Seqno_C=80]
      • Replica B 的 Seqno 向量:[Seqno_A=100, Seqno_B=95, Seqno_C=80]
      • Replica C 的 Seqno 向量:[Seqno_A=99, Seqno_B=95, Seqno_C=80](落后)
  2. ​Anti-Entropy 修复​​:

    • Replica C 发现自己的 Replica A 日志 Seqno_A=99,而其他副本是 100,于是从 Replica A 拉取 Seqno=100 的日志。
    • 修复后,所有副本的 Replica A 日志 Seqno_A=100,达成一致。

总结​

  • ​Anti-Entropy​​ 是 Abase2 实现​​最终一致性的核心机制​​,相比传统 Merkle Tree 方案更高效。
  • ​ReplicaLog + Seqno​​:
    • 每个副本维护自己的操作日志(ReplicaLog),并记录 Seqno 向量。
    • 通过比较 Seqno 向量,快速发现数据差异,并拉取缺失日志。
  • ​优势​​:
    • ​低延迟​​:秒级检测和修复。
    • ​低开销​​:主要依赖内存,避免全量扫描。
    • ​高可用​​:即使部分副本短暂不一致,也能快速恢复。

通过这种设计,Abase2 在​​多主架构​​下既保证了​​高可用性​​,又实现了​​高效的最终一致性​​。

​Seqno 和 HLC 的关系

在 Abase2 中,​​Seqno​​ 和 ​​HLC(Hybrid Logical Clock)​​ 都是用于数据版本控制和排序的机制,但它们的用途和设计目标不同。以下是它们的核心关系:


HLC(Hybrid Logical Clock)​

HLC 是一个​​全局唯一且可比较的时间戳​​,用于解决分布式系统中的​​数据冲突​​(Conflict Resolution)。

  • 在 Abase2 的多主(Multi-Leader)架构中,不同副本可能同时修改同一个 Key,需要决定哪个写入最终生效。
  • HLC 结合了物理时钟(Physical Clock)和逻辑时钟(Logical Clock),确保:
    • ​全局唯一性​​:不同副本生成的 HLC 不会冲突。
    • ​因果一致性​​:如果操作 A 发生在操作 B 之前,那么 A 的 HLC 一定小于 B 的 HLC。
    • ​近似物理时间​​:HLC 的值接近真实时间,便于业务理解(如“保留最新写入”)。

HLC 由两部分组成:

  1. ​物理时钟部分​​(高位):取自机器的本地物理时间(Unix 时间戳,毫秒/微秒级)。
  2. ​逻辑计数器部分​​(低位):用于区分同一物理时间内的多个事件。

​示例​​:

  • 副本 A 在时间 1680000000000(物理时间)写入数据,HLC 可能是 1680000000000.1
  • 副本 B 在几乎同一时间写入,HLC 可能是 1680000000000.2(逻辑部分递增)。

​HLC 在 Abase2 中的应用​

  • ​冲突解决(Last Write Wins, LWW)​​:
    • 如果两个副本同时修改同一个 Key,Abase2 会比较它们的 HLC,选择 HLC 更大的写入作为最终值。
  • ​CRDT(冲突-free 数据类型)​​:
    • 对于非幂等操作(如 IncrBy),HLC 用于对操作日志(Operation-based CRDT)排序,确保最终状态一致。

Seqno(序列号)​

Seqno 是 ​​ReplicaLog(副本日志)的严格递增序号​​,用于 ​​Anti-Entropy(反熵)机制​​,检测和修复副本间的数据不一致。

  • 每个副本维护自己的 ReplicaLog,并记录自己和其他副本的 Seqno 进度(如 [Seqno_A=100, Seqno_B=95, Seqno_C=80])。
  • Seqno ​​仅用于副本间的数据同步​​,不直接参与业务逻辑的冲突解决。

​Seqno 的特性​

  1. ​单调递增​​:每个副本的 Seqno 只会增加,不会减少。
  2. ​副本本地唯一​​:Seqno 只在单个副本的 ReplicaLog 内有意义,不同副本的 Seqno 不能直接比较(需要通过 Anti-Entropy 对齐)。
  3. ​轻量级​​:Seqno 是一个整数,存储和计算开销极低。

​Seqno 在 Abase2 中的应用​

  • ​Anti-Entropy 数据同步​​:
    • 副本间通过交换 Seqno 向量,发现谁的数据落后,并拉取缺失的日志(ReplicaLog)。
  • ​WAL(Write-Ahead Log)的日志回收​​:
    • 当多个副本在某 Seqno 前达成一致后,可以安全回收旧的日志,释放内存/磁盘空间。

Seqno 和 HLC 的关系​

​特性​ ​HLC​ ​Seqno​
​用途​ 解决数据冲突(业务层) 副本间数据同步(存储层)
​全局性​ 全局唯一,所有副本可比较 仅本地副本有意义,需通过 Anti-Entropy 对齐
​生成方式​ 混合物理时间 + 逻辑计数器 本地单调递增整数
​参与冲突解决​ 是(决定最终数据版本) 否(仅用于数据同步)
​存储位置​ 写入数据的元信息(如 Key 的版本号) ReplicaLog 的日志序号

​协作流程示例​

  1. ​写入阶段​​:

    • 用户写入 Key="foo",Value="bar",Proxy 将请求发给 Replica A。
    • Replica A 生成 HLC=1680000000000.1 作为该写入的版本号,并记录到自己的 ReplicaLog(Seqno_A=100)。
    • Replica A 将写入转发给 Replica B(Seqno_A=100, HLC=1680000000000.1)。
    • Replica B 接受写入,记录到自己的 ReplicaLog(Seqno_B=95)。
  2. ​冲突解决阶段​​:

    • 如果 Replica C 同时修改 Key="foo"(HLC=1680000000000.2),系统会比较 HLC,选择更大的写入(Replica C 的版本)作为最终值。
  3. ​Anti-Entropy 同步阶段​​:

    • Replica C 发现自己的 Replica A 日志 Seqno_A=99,而其他副本是 100,于是拉取 Seqno=100 的日志(包含 HLC=1680000000000.1 的写入)。
    • 同步完成后,所有副本的 ReplicaLog 对齐,且数据按 HLC 冲突解决规则达成一致。

总结​

  • ​HLC​​:
    • 用于​​业务层冲突解决​​(如 Last Write Wins),确保多主写入的数据最终一致。
    • 全局唯一、可比较,结合了物理时间和逻辑计数器。
  • ​Seqno​​:
    • 用于​​存储层数据同步​​(Anti-Entropy),确保副本间的数据日志(ReplicaLog)对齐。
    • 本地单调递增,不同副本的 Seqno 需通过 Anti-Entropy 机制对齐。
  • ​关系​​:
    • HLC 决定数据的最终版本,Seqno 决定数据如何同步到所有副本。
    • Seqno 同步的日志中会包含 HLC,从而保证冲突解决的正确性。

通过 ​​HLC + Seqno​​ 的组合,Abase2 实现了:

  1. ​多主写入的高可用性​​(任何副本可写)。
  2. ​低延迟的最终一致性​​(Anti-Entropy 秒级同步)。
  3. ​自动冲突解决​​(按 HLC 保留最新写入)。

3AZ 部署

​3AZ 部署​​指的是将一个系统或应用的服务实例和数据副本,​​分散部署在同一个地域(Region)内的三个不同的可用区(Availability Zone, AZ)中​​。

它是一种高可用性(High Availability)和容灾(Disaster Recovery)的最佳实践。


要完全理解 3AZ,需要先理解两个关键概念:​​地域(Region)​​ 和 ​​可用区(AZ)​​。

  1. ​地域 (Region)​

    • 地域指的是云数据中心所在的​​物理地理位置​​。每个地域都是一个独立的地理区域,通常相距很远(例如几百甚至上千公里)。
    • ​例子​​:阿里云的“华东1(杭州)”、AWS的“美国东部(弗吉尼亚北部)”、字节跳动的“华北地域”。
  2. ​可用区 (Availability Zone, AZ)​

    • 一个可用区是地域内的一个​​独立的、物理隔离的故障域​​。
    • 它由​​一个或多个物理数据中心​​组成,这些数据中心拥有​​独立的供电、冷却和网络基础设施​​。
    • 关键点在于:​​不同可用区之间通过高速低延迟的网络连接,但它们的故障域是相互隔离的​​。这意味着一个可用区的电力故障、网络中断、火灾或冷却系统失灵等事故,​​不会影响到另一个可用区的正常运行​​。
    • ​例子​​:华北地域可能有“可用区A”、“可用区B”、“可用区C”。它们可能在北京不同的园区或同一园区内不同物理建筑中。

3AZ 部署如何工作?

现在我们把“3”和“AZ”结合起来。以 Abase 这样的数据库为例:

  • 你有一份数据,为了高可用,你通常会创建 ​​3 个副本​​(Replica)。
  • 在 3AZ 部署中,你不会把这 3 个副本都放在同一个机房或同一个可用区里。
  • 相反,你会​​将每个副本分别放置到三个不同的可用区中​​:
    • 副本 1 → 可用区 A
    • 副本 2 → 可用区 B
    • 副本 3 → 可用区 C

3AZ 部署的优势和解决的问题

  1. ​抵御可用区级故障​​:这是最主要的目的。如果整个可用区A(可能因为园区级断电或网络故障)完全宕机,你的服务仍然可以依靠部署在可用区B和C的副本来继续正常运行,实现​​高可用性​​。系统会自动进行故障切换(Failover)。

  2. ​实现低延迟的容灾​​:因为三个可用区都在同一个地域内,通常通过高速光纤互联,它们之间的网络延迟非常低(通常在几毫秒内)。这使得数据可以在三个副本之间​​快速地同步​​,既保证了数据可靠性,又不会对应用性能造成太大影响。

  3. ​比跨地域部署成本更低​​:跨地域(例如从华北到华南)部署的容灾能力更强,可以抵御城市级灾难,但跨地域的网络延迟和数据传输成本通常远高于同地域内的跨AZ部署。3AZ 在​​成本和高可用之间提供了一个极佳的平衡点​​。

总结

​3AZ 部署是一种将资源(计算、存储、网络)冗余部署在同一个地域内三个相互隔离的故障域(可用区)中的架构模式。它的核心目标是提供高可用性和容灾能力,确保单一可用区的故障不会导致整个系统瘫痪,同时兼顾了低延迟和成本效益。​

在 Abase 的上下文中,它通过 3AZ 部署来保证即使某个机房出现极端故障,数据也不会丢失,服务也不会中断。


网站公告

今日签到

点亮在社区的每一天
去签到