Redis企业版 Active-Active 式地理分布

( 基于CRDT )

Home / Redis 企业版数据库 / Redis 双活架构
Active-Active 架构是一种数据弹性架构,通过独立且地理分布的集群和节点,将数据库信息分布到多个数据中心。它由多个独立处理节点组成网络,每个节点都能访问共用的复制数据库,使得所有节点均可参与同一应用程序,在保障本地访问的低延迟的同时,各区域仍能够独立运行。
在 Redis Enterprise 中,通过在使用跨多集群的全局数据库中实施 CRDT(无冲突复制数据类型,Conflict-free Replicated Data Types),可实现 Active-Active 架构(或称为 Active-Active 地理分布式拓扑)。我们将其称为 CRDB(Conflict-free Replicated Database,无冲突复制数据库)
与其他地理分布式解决方案相比,CRDB 具有三大基础优势:
  • 无论地理复制区域的数量多寡,及区域间距离多远,读写操作均具备本地级别的延迟特性。
  • 支持 Redis 核心简单与复杂数据类型的无缝冲突解决(conflict-free)。
  • 即使 CRDB 中大部分地理复制区域(例如,5 个中的 3 个)发生故障,其余地理复制区域仍能持续运行并处理读写操作,保障业务连续性。

部署与拓扑

CRDB 是在多个 Redis Enterprise 集群间创建的数据库,这些集群通常位于异地的不同数据中心。每个参与集群中的数据库称为 CRDB 实例(CRDB instance)。只要 CRDB 数据集能被写入 CRDB 实例的内存中,每个 CRDB 实例均可采用不同配置,例如由不同数量的分片组成,或是运行在不同数量或类型的集群节点上。
此外,一个 CRDB 实例可在启用高可用性和数据持久化的多可用区集群(Multi-AZ)配置中运行,而另一个 CRDB 实例可在不启用高可用性或数据持久化的单可用区集群配置中运行。这些 CRDB 可在任何云环境(公有云或私有云)中同时运行。CRDB 所具备的灵活性支持针对特定使用场景优化基础设施成本与数据库性能。

 

使用 CRDB 的应用程序会连接到本地 CRDB 实例端点。所有 CRDB 实例间采用网状拓扑进行双向数据库复制,即应用程序对本地实例的所有写入都会复制到所有其他实例,如下图所示:
注:当前 Redis Enterprise 使用全互联网状(mesh)拓扑进行 CRDB 复制,未来版本将支持如环形(ring)等更高效的拓扑结构。

高层架构

无冲突复制数据类型层(CRDT Layer)

CRDB 架构基于对大多数 Redis 命令和数据类型的替代实现(即前述的无冲突复制数据类型,Conflict-free Replicated Data Types)。在 Redis Enterprise 中,我们的 CRDB 实现基于使用 Redis Module Data Type API 构建的专有 Redis 模块。
读取命令使用本地 CRDB 实例进行处理。CRDT 层固有的无共识机制无需其他 Active-Active 实现中常见的”读取修复(read repair)”。
写入命令按照基于操作的 CRDT 原则分两步处理:
  1.  准备阶段(Prepare step):使用本地 CRDB 实例处理用户请求并生成结果效应(effect data)。
  2.  效应阶段(Effect step):将生成的效应数据分发到所有实例(包括本地实例)并应用。
Operation-based CRDT 要求所有 effect 更新以 精确一次(exactly once)源端 FIFO 一致性(source FIFO consistency) 的方式传递至所有 CRDB 实例。CRDB 在实现上依赖 Redis 原生复制机制,并经过一定改进以满足上述一致性保证。

 

高可用性与灾难恢复(HA & DR)

若一个或多个 CRDB 实例发生故障,全局 CRDB 下的其他实例将继续提供读写服务,保障持续可用性与灾难恢复。即使大多数 CRDB 实例(例如 5 个中的 3 个)宕机,剩余 CRDB 实例仍能无中断地继续处理读写操作。在此类区域性故障中,无法连接本地 CRDB 实例的用户通常会被重定向到指向可用 CRDB 实例的其他数据中心。即使用户的本地 CRDB 实例已宕机,这也为应用程序读写提供了持续可用性。
在极少数情况下,若某个 CRDB 实例发生完全数据丢失,需要从头进行数据库重同步(replication from scratch)。这种情况需特别处理:因为该实例在宕机前可能已向部分对等节点发送过更新消息,但无法保证所有节点都收到了完整更新。Redis Enterprise 为此实现了一个涉及所有相关 CRDB 实例的一致性重建机制(reconciliation mechanism)。当重建完成后,恢复中的实例即可从任一副本执行全量同步(full sync)。

一致性模型(Consistency Model)

在 CRDB 部署中同时存在多层一致性特征:
  • 本地一致性(Local consistency):当使用 WAIT 命令时,可达到接近强一致性(near-strong consistency);若未使用 WAIT,则被视为弱一致性(weak consistency)。
  • 全局一致性(Global consistency):CRDB 采用强最终一致性(Strong Eventual Consistency, SEC)模型。这意味着任意两个 CRDB 实例在接收到相同(但无序)的更新集后,最终将达到相同状态,而无需依赖共识协议。与仅提供“存活性保证(liveness guarantee)”的普通最终一致系统不同,SEC 额外提供了安全性保证(safety guarantee)

冲突解决(Conflict-free Resolution)

CRDB 的冲突解决机制遵循 CRDT(Conflict-free Replicated Data Types)的三大核心原则:
  • 并发写入结果可预测:当多个实例对同一数据对象发生并发写操作时,其结果由一套确定性规则决定。
  • 应用层无需额外处理冲突:只要应用遵循 CRDB 兼容语法(CRDB dialect),并发冲突由底层自动解决。
  • 数据最终一致收敛:所有实例的数据集最终将收敛为一个一致的全局状态。

1. 冲突解析机制

在 CRDB 中,每个实例会为每个数据对象或子对象维护一个向量时钟(vector clock)。每当该实例对对象执行更新操作,或接收到来自其他实例的同对象更新时,都会更新该对象的向量时钟。
当某个 CRDB 实例接收到来自其他实例的更新请求及其向量时钟时,会执行以下步骤:

阶段一:更新操作分类(Classify the update)

接收到的更新操作可分为三类:
  1.  新更新(new update)
  2.  旧更新(old update)
  3.  并发更新(concurrent update)
分类算法如下(假设实例 A 接收到来自实例 B 的对象 X 更新):
  • x_vc[b] > x_vc[a] → 为新更新
  • x_vc[b] < x_vc[a] → 为旧更新
  • x_vc[b] ≸ x_vc[a] → 为并发更新

其中:

  • x_vc[a] 表示对象 X 在实例 A 上的向量时钟;
  • x_vc[b] 表示对象 X 在实例 B 上的向量时钟。

阶段二:本地对象更新(Update the object locally)

根据上述分类结果:
  • 新更新(new) → 更新本地对象值;
  • 旧更新(old) → 忽略(不更新本地对象);
  • 并发更新(concurrent) → 执行冲突解决算法,判断是否及如何更新对象值。

2. CRDB 冲突解决算法

CRDB 的冲突解决策略由两类主要过程组成:

过程一:对冲突自由数据类型/操作的自动解决

在多数并发更新的情况下,数据类型自身的数学性质可保障操作无冲突(commutative、associative 或 idempotent)。以下是典型示例:
  • 计数器(Counter):所有操作都是可交换的(commutative)且无冲突。例如跨区域统计文章浏览数、分享数或转发量。
  • 集合(Set):当并发操作均为 SADD 时,所有操作可结合(associative)且无冲突。典型如在欺诈检测系统中,应用通过集合追踪特定 ID 或信用卡号的可疑事件数,当集合基数达到阈值时触发警报。
  • 哈希(Hash):当并发更新发生在不同字段(field)上时,操作相互独立,天然无冲突。例如企业共享账户下的多个用户在不同地区使用订阅计划。每个账户可包含多个用户字段,用户用量更新互不影响。
在上述情形下,本地对象将根据数据类型的内建策略自动更新,无需额外协调。

过程二:基于最后写入优先(Last Write Wins, LWW)的冲突解决

当并发更新发生在非冲突自由类型(如 Redis String)时,CRDB 采用 LWW(Last Write Wins) 策略解决冲突。该机制使用操作的时间戳(timestamp)作为仲裁依据。
值得注意的是,即使不同区域的系统时钟存在偏差,Redis Enterprise 的实现仍能保持强最终一致性(Strong Eventual Consistency):假设实例 A 的时间戳总是领先于其他实例,那么在冲突场景中实例 A 将总是“获胜”;这种偏差不会破坏全局一致性,系统最终仍会收敛至相同状态。当多个地理位置的用户同时修改同一个账号密码时,最终以最新时间戳的修改为准。这也会导致其他会话被登出,该行为在授权或许可控制场景中反而是符合预期的。

冲突解决示例

String

数据类型:String
操作用例:非并发 SET操作
冲突解决:无冲突
注:无冲突,key1 最后在 t5 被设为 “value3”

String

数据类型:String
操作用例:并发 SET操作
冲突解决:LWW(最后写入优先)
注:因 t2>t1 且基于 LWW,key1 被设为 “value2”

String

数据类型:String
操作用例:APPEND vs. DEL
冲突解决:Add Wins 新增胜利
注:APPEND 属于”更新”操作,视同”添加”操作,因此胜过 DEL 操作

String

数据类型:String
操作用例:并发过期(expire)
冲突解决:较大 TTL 优先
注:在 t6 时刻,实例 B 执行持久,其 TTL 被设为无限,大于实例 A 设置的 100

Counter

数据类型:Counter
操作用例:并发自增/自减
冲突解决:无冲突
注:在 CRDT 层,计数器值为所有操作的求和

Counter

数据类型:Counter
操作用例:删除 vs. 自增
冲突解决:Add Wins 新增胜利
注:INCRBY 属于”更新”操作,视同”添加”操作,因此胜过 DEL 操作。实例 A 在 t3 的计数器删除操作逻辑上等价于重置计数器

Set

数据类型:Set
操作用例:并发 SADD
冲突解决:无冲突
注:SADD 是满足结合律的操作

Set

数据类型:Set
操作用例:复杂并发操作(Observed Remove)
冲突解决:Add Wins 新增胜利
注:删除仅影响已观察到的元素(如 t5 时仅能删除 A、B)

PUBLISH/SUBSCRIBE

数据类型:Pub/Sub
操作用例:消息发布
冲突解决:无冲突
注:发布消息会传播至所有实例,但仅统计本地订阅者

Garbage collection

数据类型:GC
操作用例:垃圾回收
冲突解决:无冲突
注:CRDT 使用 tombstone 标记;当所有实例已观察到后自动清理

白皮书下载

连续五年被评为最受欢迎数据库!

连续五年被评为
  最受欢迎数据库!