mcpField
D
designing-multi-region-applications

designing-multi-region-applications

github.com/cockroachlabs数据与数据库23119 次安装14 周前更新
代码仓库

关于 designing-multi-region-applications

designing-multi-region-applications 是面向 CockroachDB 应用开发者的多区域架构设计技能,帮助根据读写模式选择表区域策略。它还提供延迟考量、拓扑验证方法和演示流程。

主要功能

  1. 01比较多区域表与分区模式
  2. 02根据读写模式选择区域策略
  3. 03权衡 GLOBAL 与 REGIONAL BY ROW
  4. 04验证租约持有者和区域配置
  5. 05规划多区域演示与迁移

适用场景

  1. 01为多区域应用选择数据布局
  2. 02设计跨区域读写和容灾行为
  3. 03验证区域配置后的租约分布
  4. 04准备多区域架构演示或迁移

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/cockroachlabs/cockroachdb-skills --skill designing-multi-region-applications

安装后在 Claude Code 中输入/designing-multi-region-applications确认已加载。

详细介绍

设计多区域应用程序

指导开发者为其 CockroachDB 应用程序选择合适的多区域模式,并通过适当的验证来实现。介绍如何在常规区域表、REGIONAL BY ROW、GLOBAL 表和手动地理分区之间做出选择的决策模型,并提供可用于比较不同方案的实操演示框架。

与其他技能的互补关系: 事务设计模式请参阅 designing-application-transactions。SQL 语法和架构设计请参阅 cockroachdb-sql。

何时使用此技能

  • 决定如何在 CockroachDB 中为多区域读写行为建模
  • 在主动-主动和主动-被动架构之间做出选择
  • 评估 REGIONAL BY ROW 与手动地理分区
  • 了解 GLOBAL 表的行为和权衡
  • 设计支持多个区域本地读写的方案
  • 构建或展示多区域演示或研讨会
  • 验证租约持有者位置和区域配置
  • 优化跨区域事务延迟

以下情况请勿使用此技能:问题仅涉及 SQL 语法、索引或通用架构设计,且不涉及多区域决策。

前提条件

  • 了解 CockroachDB 范围架构和租约持有者的概念
  • 拥有一个多区域集群,其节点使用 --locality=region=...,zone=... 启动,并与下方示例中使用的区域匹配。如果区域位置不匹配,DDL 会报错 region "..." does not exist,而 +region=... 之类的约束也不会匹配任何节点。本地快速搭建方式:
    # 9-node demo with three regions, three AZs each — note --no-example-database
    cockroach demo --no-example-database --nodes=9 \
      --demo-locality=region=NA-NE,az=1:region=NA-NE,az=2:region=NA-NE,az=3:region=NA-MW,az=1:region=NA-MW,az=2:region=NA-MW,az=3:region=EU-DE,az=1:region=EU-DE,az=2:region=EU-DE,az=3
    
    对于长期运行的集群,请参阅 setting-up-local-cluster,并在每次调用 cockroach start 时添加 --locality=region=...,zone=...。
  • 了解应用程序的写入模式(单区域或多区域)

模式选择

步骤 1:确定应用程序的写入模型

首先要问:写入主区域是一个还是多个?

  • 如果应用程序只有一个主要读写区域,请从主区域/常规区域表模型或手动配置的主动-被动设计入手。
  • 如果应用程序需要在多个区域实现低延迟读写,请评估手动地理分区或 REGIONAL BY ROW。
  • 如果表主要存储应在各处快速读取的参考数据,且写入路径并非关注重点,可以考虑 GLOBAL 表。
步骤 2:选择模式
A. 常规区域表(主动-被动)

适用情况:

  • 应用程序只有一个主要读写区域
  • 远程区域是次要区域或以读取为主
  • 相比在所有区域实现本地写入,更看重简单性

特点:

  • 所有租约持有者都留在活跃区域
  • 其他区域的副本可提供韧性,并在单个区域故障时维持运行
  • 参考延迟:写入约 20 ms,读取约 2~5 ms(本地区域)

建议: 优先采用更高层级的多区域抽象,除非用户明确需要手动控制分区、投票副本和租约偏好。

B. 手动地理分区并指定区域特定的租约持有者

适用情况:

  • 应用程序采用主动-主动模式
  • 数据模型按区域划分键
  • 团队希望拥有明确的运维控制权
  • 了解内部机制(分区、投票副本、租约偏好)很重要

特点:

  • 区域特定的租约持有者模式可将写入延迟保持在约 20 ms,读取延迟保持在约 2~5 ms
  • 应用程序必须确保某个键的读写操作都在同一区域进行
  • DDL 和运维负担更重
  • 最适合用于讲解内部机制

DDL 示例:

CREATE TABLE accounts_manual (
  account_id STRING(40),
  owner_id   STRING(40) NOT NULL,
  status     STRING(20) NOT NULL,
  region     STRING(10) NOT NULL,
  CONSTRAINT accounts_manual_pkey PRIMARY KEY (region, account_id)
);

ALTER INDEX accounts_manual_pkey
  PARTITION BY LIST (region) (
    PARTITION na_ne VALUES IN ('NA-NE'),
    PARTITION na_mw VALUES IN ('NA-MW'),
    PARTITION na_nw VALUES IN ('NA-NW')
  );

ALTER PARTITION na_ne OF INDEX accounts_manual_pkey
  CONFIGURE ZONE USING
    num_replicas      = 5,
    num_voters        = 5,
    voter_constraints = '{+region=NA-NE: 2, +region=NA-MW: 2, +region=NA-NW: 1}',
    lease_preferences = '[[+region=NA-NE]]';
C. REGIONAL BY ROW

适用场景:

  • 工作负载采用主动-主动模式
  • 每行数据天然归属于某个区域
  • 团队希望在多个区域实现本地读写,而无需手动管理分区和可用区配置
  • 目标是提供面向开发者的多区域抽象

特点:

  • 所有已配置的区域都可能成为主区域或租约持有者所在区域
  • 参考延迟:写入约 20ms,读取约 2–5ms(本地区域)
  • 比地理分区所需的手动配置更少
  • 对于区域亲和型应用数据,这是默认推荐方案

DDL 示例:

CREATE DATABASE IF NOT EXISTS example_service_rbr;
ALTER DATABASE example_service_rbr PRIMARY REGION 'NA-NE';
ALTER DATABASE example_service_rbr ADD REGION 'NA-NW';
ALTER DATABASE example_service_rbr ADD REGION 'NA-MW';
ALTER DATABASE example_service_rbr SURVIVE REGION FAILURE;

USE example_service_rbr;

CREATE TABLE accounts_rbr (
  account_id STRING(40),
  owner_id   STRING(40) NOT NULL,
  status     STRING(20) NOT NULL,
  region     crdb_internal_region
    NOT NULL
    DEFAULT gateway_region()::crdb_internal_region,
  CONSTRAINT accounts_rbr_pkey PRIMARY KEY (region, account_id)
) LOCALITY REGIONAL BY ROW AS region;

本地分配模式:

WITH candidate AS (
  SELECT id, resource_code
  FROM resource_pool
  WHERE allocated_at IS NULL
    AND region = gateway_region()::crdb_internal_region
  ORDER BY random()
  LIMIT 1
  FOR UPDATE
)
UPDATE resource_pool
SET allocated_at = now()
WHERE id = (SELECT id FROM candidate);
D. GLOBAL 表

适用场景:

  • 表存储的是全局/参考类数据
  • 工作负载主要关注广泛的读取本地性,而不是由区域负责写入

重要限制:GLOBAL 表会优化各处的快速读取。未经官方文档确认具体产品行为时,不要将其描述为“各处均可读写”的模式。

E. 生存目标

根据写入延迟与持久性之间的权衡选择生存目标:

-- Survive any single zone failure (default, 3+ zones required):
ALTER DATABASE mydb SURVIVE ZONE FAILURE;

-- Survive an entire region going down (3+ regions required):
ALTER DATABASE mydb SURVIVE REGION FAILURE;
目标要求写入延迟数据安全
SURVIVE ZONE FAILURE3+ 个可用区低(本地共识)可承受 1 个可用区中断
SURVIVE REGION FAILURE3+ 个区域较高(跨区域共识)可承受 1 个区域中断

SURVIVE REGION FAILURE 会增加写入延迟,因为 Raft 共识必须跨越多个区域,但即使整个云区域离线,也能保证零数据丢失。

模式对比
方面常规区域模式手动地理分区REGIONAL BY ROWGLOBAL
写入模式单个主区域主动-主动,按区域键划分主动-主动,行亲和从主区域写入
读取本地性主区域本地分区本地行所在区域本地所有区域
运维负担低高中低
配置最少显式分区、可用区和租约偏好数据库级抽象表级声明
最适用场景简单的主区域应用需要完全控制底层机制面向开发者的多区域应用参考数据

实时演示设置

在研讨会和技术演示中,使用 9 节点本地演示集群,让多区域本地性直观可见。

集群设置
cockroach demo \
  --nodes 9 \
  --no-example-database \
  --insecure \
  --demo-locality=\
region=NA-NE,zone=NA-NE-1:\
region=NA-NE,zone=NA-NE-2:\
region=NA-NE,zone=NA-NE-3:\
region=NA-MW,zone=NA-MW-1:\
region=NA-MW,zone=NA-MW-2:\
region=NA-MW,zone=NA-MW-3:\
region=NA-NW,zone=NA-NW-1:\
region=NA-NW,zone=NA-NW-2:\
region=NA-NW,zone=NA-NW-3
演示流程

推荐的演示顺序:

  1. 从手动地理分区方案开始
  2. 展示显式分区和可用区配置
  3. 运行验证查询并确认租约归属
  4. 切换到 REGIONAL BY ROW
  5. 运行 RBR 验证
  6. 对比运维范围
验证查询

手动分区验证:

SHOW RANGES FROM INDEX accounts_manual_pkey WITH DETAILS;

检查以下内容:

  • 所有预期的分区值均已存在
  • 租约持有者的本地性与分区区域相符
  • 不匹配时返回 FAIL,否则返回 PASS

RBR 验证:

SHOW RANGES FROM TABLE accounts_rbr WITH DETAILS;

检查以下内容:

  • 租约持有者的本地性覆盖了所有预期区域
  • 不存在非预期的租约区域
演示要点

手动方案:

  • 可精准控制分区、投票者、副本和租约偏好
  • DDL 更多,运维负担更重
  • 最适合讲解内部机制,以及理解数据库底层的工作方式

RBR 方案:

  • 让应用意图始终成为关注重点
  • 手动配置更少
  • 更便于向应用团队讲解
  • 仍基于相同的拓扑结构

跨区域延迟指南

当客户端与相关租约持有者/仲裁路径位于不同位置时,事务延迟会增加。

客户端位置本地读写延迟跨区域读写延迟
与租约持有者位于同一区域~10-20ms—
位于其他区域—~50-150ms+

指南:

  • 将对延迟敏感的服务部署在靠近其主要数据位置的地方
  • 对非关键的展示/报告查询使用跟随者读取
  • 有意使用多区域表的本地性和区域配置
  • 不要假设“分布式”就意味着“各处延迟都相同”

输出预期

使用此技能给出的优质回答应包括:

  1. 推荐的模式
  2. 该模式为何适合此工作负载
  3. 应用程序必须执行的操作(路由、行亲和性、主区域假设)
  4. 哪些由 CockroachDB 自动管理,哪些需要手动管理
  5. 预期的延迟特征或本地性行为
  6. 用户要求的特性若非所选模式的优化目标,应发出警告

防护准则

  • 不要声称常规主区域表可从所有区域实现对称的低延迟写入
  • 没有文档支持时,不要声称 GLOBAL 能满足所有区域低延迟写入的需求
  • 比较手动地理分区与 REGIONAL BY ROW 时,明确指出控制力与简易性之间的取舍
  • 用户想了解内部机制时,优先先解释手动模型
  • 用户想采用最佳默认应用模式时,对于区域亲和型数据,优先推荐 REGIONAL BY ROW
  • 所有 SQL 中的区域名称和本地性标签保持一致
  • 除非明确进行比较,否则不要在同一说明中混用手动方法和抽象方法
  • 始终包含验证步骤,而不只是 DDL

多区域迁移检查清单

对于从单区域 PostgreSQL/Oracle 迁移到多区域 CockroachDB 的团队:

  1. 使用 --locality=region=<region>,zone=<zone> 部署节点
  2. 设置主区域:ALTER DATABASE <db> PRIMARY REGION '<region>'
  3. 添加区域:ALTER DATABASE <db> ADD REGION '<region>'(每个区域都执行一次)
  4. 设置生存目标:ALTER DATABASE <db> SURVIVE ZONE|REGION FAILURE
  5. 对表进行分类:GLOBAL(参考数据)、REGIONAL BY ROW(行亲和型)、REGIONAL BY TABLE(默认)
  6. 设置本地性:ALTER TABLE <t> SET LOCALITY <locality>
  7. 在 DB Console 中监控租约持有者分布
  8. 测试故障转移:关闭一个可用区/区域,并验证生存目标仍能满足

安全注意事项

  • 多区域配置变更会影响整个集群中的数据放置
  • 在生产环境部署前,先在演示或预发布集群上测试多区域配置
  • 配置变更后验证租约持有者的放置情况
  • 拓扑变更后,留出时间让范围重新平衡

参考资料

评论

支持 Markdown