CockroachDB 多区域指南:表本地性、延迟与容灾取舍

数据库

CockroachDB 可以让 SQL 数据库跨区域运行,但多区域数据库不是魔法。它可以提升韧性,并在数据靠近用户时降低延迟;同时也可能带来写入延迟、事务冲突、运维复杂度和数据驻留设计问题。

这篇文章关注 CockroachDB 多区域设计中的实际选择:应该添加哪些区域、如何选择表本地性、什么时候用 regional by row、什么时候用 global table,以及生产前应该验证什么。

参考文档:

最重要的设计问题

改 SQL 之前,先确认应用最需要什么。

目标 设计方向 取舍
降低区域用户读取延迟 把数据放到主要读取它的区域附近 跨区域写入仍可能更慢
数据驻留 使用区域放置和明确的数据归属 Schema 和路由必须严格
全球参考数据 对读多写少的小表使用 global table 写入通常需要更谨慎
区域故障容忍 选择合适的 survival goal 和拓扑 更多副本和协调可能增加延迟
多区域活跃用户 流量路由到近端区域,并建模数据归属 冲突写入仍需应用处理

好的多区域设计从访问模式出发,而不是只从地图出发。

核心概念

CockroachDB 多区域抽象围绕这些概念:

  • Cluster region:节点启动时通过 locality 设置的区域。
  • Database region:加入到数据库的区域,其中一个是 primary region。
  • Primary region:数据库默认使用的区域,除非表本地性另有设置。
  • Table locality:CockroachDB 如何放置和优化表数据。
  • Survival goal:数据库设计上可承受的故障范围。
  • Secondary region:部分配置中用于故障切换相关的区域。

这些设置会影响性能和可用性,不只是部署标签。

基础多区域配置

多区域集群从节点 locality 开始。每个节点应知道自己的 region 和 zone。

cockroach start \
  --locality=region=us-east-1,zone=us-east-1a \
  --certs-dir=certs \
  --join=node1:26257,node2:26257,node3:26257

然后配置数据库区域。

ALTER DATABASE global_app SET PRIMARY REGION "us-east-1";
ALTER DATABASE global_app ADD REGION "eu-west-1";
ALTER DATABASE global_app ADD REGION "ap-southeast-1";

SHOW REGIONS FROM DATABASE global_app;

这不会自动让所有查询在所有地方都变快。它只是建立区域基础。用户能感受到的延迟,更多取决于表本地性和应用路由。

选择表本地性

表本地性决定数据如何放置和优化。

Locality 适合场景 说明
Regional by table 整张表主要属于一个区域 适合区域专属表
Regional by row 不同行属于不同区域 适合用户、租户或账户数据
Global 小型读多写少参考数据 读体验好,写入要更谨慎

Regional By Table

当整张表主要属于一个区域时,使用 regional by table。

CREATE TABLE eu_tax_rules (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    country STRING NOT NULL,
    rule JSONB NOT NULL
) LOCALITY REGIONAL BY TABLE IN "eu-west-1";

它适合区域配置、法律规则、本地目录,或与某个地理区域绑定的运营数据。

Regional By Row

当每一行都有自己的 home region 时,使用 regional by row。用户或租户数据很常见。

CREATE TABLE users (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    email STRING NOT NULL,
    home_region crdb_internal_region NOT NULL,
    created_at TIMESTAMPTZ NOT NULL DEFAULT now()
) LOCALITY REGIONAL BY ROW;

应用必须正确设置并维护 home region。不要把区域字段当成普通附属字段,它是数据模型的一部分。

Global Tables

global table 适合各地都要读、但很少写的小表。

CREATE TABLE countries (
    code STRING PRIMARY KEY,
    name STRING NOT NULL
) LOCALITY GLOBAL;

适合国家代码、很少变动的功能开关、公共配置和参考数据。不要把高频交易写入表默认设为 global,除非已经测试过延迟和冲突行为。

有意设计流量路由

数据库放置只是设计的一半。应用流量通常也应路由到最近的健康区域。

需要检查:

  • 每个用户请求由哪个应用区域处理。
  • 连接池是否连接到本地数据库节点。
  • 读取流量是否靠近用户数据 home region。
  • 写入是否发送到拥有该行或租户的区域。
  • 故障切换后,流量是否进入可以安全服务的区域。

如果欧洲用户访问欧洲应用服务器,但该服务器写入 home 在美国的行,数据库无法完全隐藏跨区域延迟。

处理事务冲突

多区域不会消除写入归属设计。如果两个区域频繁更新同一行,仍可能出现重试和竞争。

降低冲突风险的方法:

  • 为每个租户、账户或文档指定清晰写入归属。
  • 避免热点全局计数器。
  • 使用幂等命令和可安全重试的事务逻辑。
  • 缩短事务时间。
  • 将高写入记录拆成更小的归属单元。
  • 把读多共享数据放进参考表。

Serializable 隔离很有价值,但应用仍然需要明确的重试策略。

数据驻留

如果有数据驻留要求,要明确哪些数据必须留在哪些区域:

  • 用户资料。
  • 支付或账单数据。
  • 日志和审计记录。
  • 备份。
  • 派生分析数据。
  • 搜索索引和缓存。

正确放置主表还不够。副本、导出、日志、备份和下游系统也必须遵守同一策略。

Survival Goals

Survival goal 定义数据库设计上要承受什么故障。更高韧性通常需要更谨慎的拓扑,也可能影响延迟。

需要问:

  • 需要承受 zone 故障,还是完整 region 故障?
  • 有多少 region 和 zone 可用?
  • 哪些表在故障时仍必须可写?
  • 当前 table locality 是否和相关特性兼容?
  • 是否用真实应用流量做过故障切换测试?

在没有测试之前,不要在架构文档里承诺固定 RTO 或 RPO。

生产检查清单

上线前确认:

  • 节点启动时 region 和 zone 配置一致。
  • 数据库 primary region 和 secondary region 是有意设计的。
  • 每张表按访问模式选择 locality。
  • regional by row 表有可靠的区域赋值。
  • 应用路由匹配数据归属。
  • 事务重试逻辑已经实现。
  • 备份和下游系统遵守驻留规则。
  • Schema 变更从合适区域执行。
  • 监控覆盖延迟、重试、leaseholder 变化和 unavailable range。
  • 故障演练有文档并且会重复执行。

常见错误

把所有表都设成 Global

Global table 有用,但不是默认选项。它适合小型读多写少数据。高写入业务表必须谨慎测试。

忽略应用层

如果应用流量没有按区域路由,数据库本地性无法完全解决延迟。

缺少重试逻辑

分布式 SQL 系统需要应用处理可重试事务错误。重试应明确,并且操作要幂等。

把数据驻留当成单表设置

数据驻留包括备份、日志、分析、搜索和导出。要审计完整数据流。

不做故障测试

多区域设计应在节点、zone 和 region 故障场景下测试。不要假设架构图等于生产行为。

总结

CockroachDB 多区域设计的核心是取舍:让数据靠近用户、满足驻留要求、承受故障,并保持写入路径可理解。先从访问模式和数据归属开始,再选择表本地性、配置区域、实现重试并测试故障切换。

目标不是“所有地方都 active-active”,而是在正常流量和区域故障时都有可预期的行为。

本站提供浏览器本地工具,免注册即可试用 →

#CockroachDB多区域#多活数据库#全球分布式#Geo分区#2026#数据库