dot 速度的未来正在您所在的城市举办的活动中到来。

加入我们在 Redis 发布会上

主动-主动架构是一种数据弹性架构,通过独立且地理上分布的集群和节点,将数据库信息分布在多个数据中心。它是一个由独立的处理节点组成的网络,每个节点都可以访问一个共同复制的数据库,从而使所有节点都能够参与一个共同的应用程序,确保每个区域都能够独立运行,从而实现本地低延迟。

通过在 Redis 企业版中使用跨越多个集群的全局数据库实现 CRDT (无冲突复制数据类型),可以实现主动-主动架构或主动-主动地理分布拓扑结构。这被称为“无冲突复制数据库”或“CRDB”。

与其他地理分布式解决方案相比,CRDB 提供了三个主要优势

  • 它提供本地延迟的读写操作,无论地理复制区域的数量和它们彼此之间的距离如何。
  • 它为简单和复杂数据类型(例如 Redis 核心数据类型)提供无缝的冲突解决(“无冲突”)。
  • 即使 CRDB 中的大多数地理复制区域(例如,5 个中的 3 个)都处于停机状态,剩余的地理复制区域也不会中断,可以继续处理读写操作,确保业务连续性。

部署和拓扑结构

CRDB 是一个跨多个 Redis 企业版集群创建的数据库,这些集群通常位于世界各地的不同数据中心。参与的每个集群中的数据库被称为“CRDB 实例”。只要 CRDB 数据集适合 CRDB 实例内存,每个 CRDB 实例都可以通过不同的方式配置,即由不同数量的碎片组成,并在不同数量或类型的集群节点上运行

此外,一个 CRDB 实例可以在具有高可用性和数据持久性的多可用区 (AZ) 集群配置上运行。第二个 CRDB 实例可以在没有高可用性或数据持久性的单个 AZ 集群配置上运行。这些 CRDB 可以同时在任何云环境中运行,包括 Google Cloud、Azure 和 AWS。这种增加的灵活性优化了基础设施成本和数据库性能,以满足您的特定用例。

使用 CRDB 的应用程序连接到本地 CRDB 实例端点。所有 CRDB 实例之间使用双向数据库复制,以网格状拓扑结构进行,即应用程序对本地实例的所有写入都会复制到所有其他实例,如下所示

注意:Redis 企业版的未来版本将提供额外的拓扑结构,例如环状拓扑结构。

高级架构

无冲突复制数据类型层

CRDB 架构基于大多数 Redis 命令和数据类型的替代实现(如上所述,称为无冲突复制数据类型)。在 Redis 企业版中,我们的 CRDB 实现基于使用 Redis 模块数据类型 API 构建的专有 Redis 模块。

读取命令使用本地 CRDB 实例本地处理。CRDT 层的固有无共识机制不需要其他主动-主动实现中常见的“读取修复”。 

写入 命令按两步处理,遵循基于操作的 CRDT 的原则

  1. 在 准备 步骤中,使用本地 CRDB 实例处理用户的请求,并创建一个结果 效果 。
  2. 在 效果 步骤中,将上面生成的 效果 数据分发到所有实例(包括本地实例)并应用。

基于操作的 CRDT 需要将效果更新以保证一次且仅一次地传递到所有 CRDB 实例,并以源 FIFO 一致性进行传递。CRDB 通常依赖于 Redis 复制机制,并进行了一些修改以满足这些保证。

对等复制

CRDB 复制通过同步器实现,同步器联系远程主节点并请求对等复制(如下所示),并引入了专门为此目的而设计的新数据库复制机制。对等复制与标准 Redis 复制类似

  • PSYNC 和复制偏移量用于恢复断开的链接
  • 始终可以使用完全 SYNC 作为备用

对等节点建立复制链接后,只传播 CRDT 模块生成的 CRDT 效果。可以应用额外的过滤,以根据拓扑结构仅包含特定更新。当使用网格拓扑结构时,对等复制将只携带在本地 CRDB 实例上生成的效应。

CRDB 实例可以将不同的复制流推送到其他对等节点,以便对等复制机制可以管理许多不同的复制积压。

自动 GZIP 压缩和 SSL 加密

当识别出远程公共 IP 时,GZIP 压缩会自动应用于对等复制,以更好地利用广域网链接。此外,如果在连接建立期间识别出 SSL 握手,则会自动设置加密。

对等复制操作说明如下

高可用性和灾难恢复

如果一个或多个 CRDB 实例发生故障,全局 CRDB 下的其他实例将继续读取和写入,提供持续的可用性和灾难恢复。即使大多数 CRDB 实例(例如,5 个中的 3 个)都处于停机状态,剩余的 CRDB 实例也不会中断,可以继续进行读取和写入。在这些区域性故障情况下,无法连接到本地 CRDB 实例的用户通常会被重定向到指向可用 CRDB 实例之一的另一个数据中心。这为应用程序的读取和写入提供了持续可用性,即使用户的本地 CRDB 实例处于停机状态。

在极少数情况下,CRDB 实例可能会遇到完全的数据丢失,需要从头开始进行数据库复制。这种情况需要特殊处理,因为恢复的 CRDB 实例可能已将更新发送到其一些对等节点。由于无法预期进一步的更新,因此我们无法假设所有对等节点最终都会收敛(一些效应消息可能已被一些对等节点接收,但其他对等节点没有收到)。在这种情况下,Redis 企业版实施了一个协调机制,涉及所有相关的 CRDB 实例。协调完成后,恢复的实例只需从任何其他副本进行完全同步。

一致性模型

在 CRDB 部署中应用了多种一致性特征

  • 对于本地 CRDB 实例操作,当实施 WAIT 命令时,可以实现近似强一致性;否则,操作将被归类为具有弱一致性。
  • 对于全局 CRDB 操作,强最终一致性 (SEC) 特征确保任何两个已收到相同(无序)更新集的 CRDB 实例都将处于相同状态,而无需使用共识协议。与仅提供 活性保证(即,更新最终会观察到)的最终一致性系统不同, 强最终一致性添加了 安全保证。

冲突(免费)解决

CRDB 冲突解决基于三个 CRDT 原则

  • 并发写入的结果是可预测的,并且基于一组规则。
  • 应用程序不需要对并发写入进行任何操作(但必须与 CRDB“方言”兼容)。
  • 数据集最终将收敛到一个单一的一致状态。

冲突解决的工作原理

每个 CRDB 实例都单独维护一个 向量时钟,用于每个数据集对象/子对象。在实例级别进行任何更新操作或从其他 CRDB 实例接收到同一个对象的另一个更新操作时,都会更新此向量时钟。

从另一个实例接收到更新操作(和向量时钟)后,每个 CRDB 实例都会单独执行以下过程

阶段 1: 对更新操作进行分类

接收到的更新操作可以表示 (1) 新更新、(2) 旧更新或 (3) 并发更新。

分类算法的工作原理如下

当实例 A 从实例 B 接收有关对象 X 的更新时

  • 如果:x_vc[b] > x_vc[a] - 这是“新”更新
  • 如果:x_vc[b] < x_vc[a] - 这是“旧”更新
  • 如果:x_vc[b] ≸ x_vc[a] - 这是“并发”更新

其中 x_vc[a] 是实例 A 中对象 X 的向量时钟,x_vc[b] 是实例 B 中对象 X 的向量时钟。

阶段 2: 在本地更新对象

  • 如果更新被分类为“新”,则在本地 CRDB 实例中更新对象值
  • 如果更新被分类为“旧”,则不要在本地 CRDB 实例中更新对象值
  • 如果更新被分类为“并发”,则执行冲突解决以确定是否以及如何在此 CRDB 实例中更新对象值

CRDB 冲突解决算法基于两个主要过程

过程 1:针对无冲突数据类型/操作的冲突解决

在许多并发更新状态情况下,可以根据适用数据类型的属性,完全无冲突地处理更新。以下是一些示例

  • 当数据类型为计数器(映射到 CRDT 的计数器)时,所有操作都是可交换的且无冲突的。示例:跨区域跟踪文章受欢迎程度、分享或转发次数的计数器。
  • 所有操作要么是结合性的,要么是幂等的,并且在数据类型为集合(映射到 CRDT 的 Add-Wins Observed-Removed 集合)时,且并发更新为 ADD 操作时,这些操作是无冲突的。**示例:**在欺诈检测中,当应用程序跟踪与 ID 或信用卡相关的可疑事件时。与 ID 或信用卡关联的 Redis 集合基数用于在达到阈值时触发警报。
  • 当数据类型为哈希(映射到 CRDT 的映射)且并发更新在不同的哈希字段上时,所有操作都是无冲突的,就好像它们是在不同的对象上实现的一样。**示例:**一个共享的企业帐户,多个用户使用不同的计划,在不同的地点或以不同的费率使用。每个帐户的 HASH 对象可以包含每个用户的无数字段,即使在并发情况下,也可以无冲突地更新单个用户的使用情况。

在所有这些情况下,本实例中的对象值根据数据类型策略进行更新。

**过程 2**:使用最后写入者获胜 (LWW) 机制解决冲突

在非无冲突数据类型(如 Redis 字符串(映射到 CRDT 的寄存器))中出现并发更新的情况下,应该应用冲突解决算法。我们使用 LWW 方法来解决此类情况,方法是利用操作时间戳作为决胜局。

请注意,我们的解决方案以强最终一致的方式工作,即使区域之间存在时间戳偏差。例如,假设实例 A 的时间戳始终领先于其他实例的时间戳(即,在决胜局中,实例 A 始终获胜)。这确保了最终一致的行为。**示例:**为由多个地理分布式实体访问的用户帐户更改密码。在这种情况下,更改将使其他用户注销,这可能是许可证执行场景的正确行为

冲突解决示例

字符串

**数据类型:** 字符串
**用例:** 非并发 SET
**冲突解决:** 无冲突

注意:无冲突,key1t5 时最后设置为“value3”。

**数据类型:** 字符串
**用例:** 并发 SET
**冲突解决:** 最后写入者获胜 (LWW)

注意:t2>t1 并且由于 LWW,key1 设置为“value2”。

**数据类型:** 字符串
**用例:** APPEND 与 DEL
**冲突解决:** 添加获胜

注意:APPEND 是一个“更新”操作,被视为“添加”,因此比 DEL 操作更胜一筹。

**数据类型:** 字符串
**用例:** 并发过期
**冲突解决:** 更大的 TTL 获胜

注意:在 t6 时,实例 B 持久化(使用 PERSIST key1),这意味着它的 TTL 设置为无穷大(即 -1),大于 实例 A 设置的 100

计数器

**数据类型:** 计数器
**用例:** 并发增量/减量操作
**冲突解决**:无冲突

注意:在 CRDT 层,计数器值是所有操作的总和。

**数据类型:** 计数器
**用例:** 并发删除和增量操作(在计数器值中观察到的删除)
**冲突解决:** 添加获胜

注意:INCRBY 是一个“更新”操作,被视为“添加”,因此比 DEL 操作更胜一筹。此外,实例 At3 时执行的计数器删除操作在逻辑上意味着重置计数器。

集合

**数据类型:** 集合
**用例:** 对集合执行并发 SADD 操作
**冲突解决:** 无冲突

注意:此示例是无冲突的,因为 SADD 是一个结合操作。

**数据类型:** 集合
**用例:** 对集合执行并发 SADD 和 SREM 操作
**冲突解决:** 添加获胜

注意:在集合元素中,添加获胜。

**数据类型:** 集合
**用例:** 对集合执行并发复杂操作(观察到的删除)

**冲突解决:** 在集合元素中,添加获胜

注意:在 t5 时,实例 A 只能从 key1 中删除元素 AB

发布/订阅

**数据类型:** 发布/订阅
**用例:** 发布的消息传播到所有 CRDB 实例
**冲突解决:** 无冲突

注意:在PUBLISH 响应中,只计算本地订阅者。

垃圾回收

**数据类型:** 字符串
**用例:** 垃圾回收
**冲突解决:** 无冲突

注意:CRDT 广泛使用墓碑。墓碑在被所有实例观察到后会变成垃圾。


下一节 ► 自动分层