多区域(Multi-Region)与多租户(Multi-Tenancy)是全球化业务系统设计的两个核心维度。它们解决的是不同的问题:多区域关注物理位置与容灾,多租户关注资源与数据的归属和隔离。但在真实业务中二者常常叠加出现——同一个微服务集群既要按区域就近部署,又要按租户做隔离。本文从网关、应用层到存储中间件,梳理一套可落地的实现思路。
为什么需要多区域 + 多租户
以全球化电商/支付平台为例,业务通常面临两类约束:
- 区域约束:用户分布在全球,就近接入降低延迟;单个机房故障不能拖垮全部业务;部分地区有数据本地化法规(如 GDPR、印尼数据本地化),数据必须留在特定区域。
- 租户约束:一套系统要服务多个"租户",租户可以是内部业务线、品牌商家、甚至独立的第三方商户。不同租户的资源配额、数据可见性、灰度节奏都不同。
更复杂的在于:区域和租户不是两个独立正交的维度。一个租户可能只在某个国家运营(如东南亚某国本地商户),也可能横跨整个大洲(如北美、南美按区域运营)。因此需要一种机制,让"运营边界"(Region / Country / Tenant)可以灵活定义,并从网关一直贯穿到存储。
核心概念
- Region(区域):物理部署单元,如
ap-southeast、us-east、sa-east。一个 Region 是一组自洽的基础设施(K8s 集群、数据库、MQ、缓存)。 - Country / Market(国家/市场):业务运营单元,如新加坡、印尼、巴西。通常一个 Country 拥有独立的币种、支付渠道与合规策略。
- Tenant(租户):资源与数据归属的最小业务单位,如某个商家、某条业务线。
这三者不是严格的包含关系:Region 可以覆盖多个 Country,一个 Country 也可以承载多个 Tenant;反之,一个大洲级租户(如北美品牌)可能把多个 Country 合并成一个"运营区域"。
管理单元(Admin Unit):统一运营边界抽象
设计上的第一步是把"运营边界"抽象为管理单元(Admin Unit),它可以是 Region、Country 或 Tenant 的任意一级。这样后端系统不必各自实现一套"区域判断"逻辑,而是统一用管理单元来路由、隔离与计费。
Admin Unit(管理单元)
├── type: REGION 例如 ap-southeast、us-east、sa-east
├── type: COUNTRY 例如 sg、id、br
└── type: TENANT 例如 brand-a、merchant-001
关键点:
- 支持混合粒度:东南亚按国家运营(
COUNTRY),北美/南美按区域运营(REGION),租户内部又可以有多个TENANT。管理单元树支持任意组合。 - 链路中只传 Admin Unit ID:网关解析出
admin_unit_id后,放入请求上下文透传,全链路只认这个 ID,不再各自判断国家/区域。 - 元数据驱动:Admin Unit 的属性(所属 Region、Country、租户配额、数据本地化要求、灰度标签)存在配置中心或专门的元数据服务中,支持动态调整。
架构分层
入口网关层
网关(API Gateway)是多区域多租户的第一道分流点,职责包括:
- 租户识别:从 Token / Header 解析出
tenant_id和admin_unit_id,完成认证后写入请求上下文。 - 区域路由:根据
admin_unit_id的元数据(归属 Region),将请求转发到对应区域的集群;无归属信息的请求走默认 Region。 - 策略注入:按管理单元注入限流配额、灰度标识、自定义 Header。
- 地域就近:根据客户端地理位置(GeoIP)做 DNS/入口层就近调度,但业务路由仍以 Admin Unit 为准——用户所在的 Region 和其操作的数据归属 Region 可能不同。
应用层(微服务)
- 上下文透传:网关注入的
admin_unit_id/tenant_id通过 RPC Header(gRPC metadata / OpenTelemetry baggage / HTTP header)透传到每个微服务,服务内部放入 ThreadLocal / ScopedContext。 - 多区域部署:同一套微服务镜像部署到多个 Region,通过区域注册中心(每个 Region 一套注册中心,或注册中心带 Region 标签)实现区域自治:服务默认只调用本 Region 的实例。
- 租户隔离:在服务内按
tenant_id做数据访问过滤(下面存储层会讲),同时租户级熔断/限流,防止单租户打垮共享资源。 - 跨区调用:原则上禁止同步跨 Region 调用(避免跨洋延迟与故障放大),跨区数据传递走 MQ/异步。
数据层
数据是多区域多租户设计中最难的部分,通常分两类处理:
- 全局共享数据:租户元数据、Admin Unit 配置、汇率、字典表。这类数据量小、只读为主,可放全局配置中心 + 各 Region 缓存副本。
- 业务数据:订单、交易流水、用户数据。按
tenant_id分片,同时按 Region 落地。
常见做法:
- 分库分表:分片键优先选
tenant_id(保证租户数据不跨片),同时按 Region 建独立库;主键用"租户感知"的 ID(如tenant_id + 雪花ID)避免分片后全局唯一性问题。 - 读写分离 + 跨区复制:每个 Region 主库本地读写,通过 binlog/CDC 异步复制到其他 Region 作为只读副本或灾备。
- 多主(Multi-Master):强一致要求高的场景,每个 Region 都可写,需要解决冲突(如按 Region 优先级、版本号/LWW 合并)。大多数业务用"单写多读 + 主从复制"即可,避免多主复杂度。
- 缓存:缓存 key 带
tenant_id前缀天然隔离;Region 内缓存优先,不跨区共享缓存。 - 数据本地化:对要求数据驻留(Data Residency)的租户,用 Admin Unit 的 Region 属性强制其所有数据只落指定 Region,路由层做校验拦截。
中间件与基础设施
- 消息队列:Topic 按租户/区域分区,如
order.<region>.<tenant>;跨区同步用独立 Topic + 消费桥接。 - 注册与发现:Region 自治,服务发现默认本 Region;跨区服务的调用统一走网关或数据面,不直连。
- 配置中心:配置按
默认值 → Region → 租户三级覆盖,支持运行期动态调整。 - 监控与日志:指标和日志打上
region、tenant_id标签,便于跨区追踪(配合 TraceId 串联全链路)。
典型场景示例
场景一:东南亚按国家运营
印尼(id)、新加坡(sg)、泰国(th)各自独立运营:币种不同、支付渠道不同、合规不同。此时 Admin Unit 用 COUNTRY 粒度:
ap-southeast(Region)
├── COUNTRY id → 印尼币种 IDR、本地支付
├── COUNTRY sg → 新加坡币种 SGD、本地支付
└── COUNTRY th → 泰国币种 THB、本地支付
网关按请求中的国家上下文路由到对应 COUNTRY 的实例,数据按国家分库,互不混淆。
场景二:北美/南美按区域运营
北美多个国家(US、CA、MX)业务模式接近,合并成一个运营区域;南美(BR、AR、CL)也按区域运营。此时 Admin Unit 用 REGION 粒度:
us-east(Region,覆盖 US/CA/MX)
sa-east(Region,覆盖 BR/AR/CL)
区域内的国家共享币种策略或只做小差异化(差异放配置),路由和隔离都在 Region 层完成。
设计原则与避坑
- 先抽象 Admin Unit,再谈隔离:不要让每个微服务各自硬编码"国家/区域"判断,统一用管理单元 ID 贯穿。
- 同步禁止跨区,异步尽量跨区:跨 Region 的强一致调用是性能与可用性黑洞,用 MQ/事件最终一致替代。
- 分片键要稳定:
tenant_id一旦定下就别换,分库分表后的扩容/迁移成本极高,一开始就为租户量预留余量。 - 隔离不是全隔离:共享存储换灵活性,隔离靠租户级限流 + 数据过滤 + 配额兜底,而不是每个租户一套独立部署(除非有强合规要求)。
- 监控打标签:没有
region/tenant_id标签的监控,在故障时无法快速定位"哪个区域的哪个租户"出问题。
参考
- AWS Well-Architected: Multi-Region Fundamentals
- MySQL binlog / CDC 跨区复制实践
- GDPR 与数据本地化法规对数据驻留的要求