SET化架构:从单元化原理到大规模落地实践

SET化架构:从单元化原理到大规模落地实践

当系统规模突破单机房、单集群的承载极限,当一次机房故障就可能导致全站不可用时,SET 化架构就成为了必然选择。它不是一种特定的技术方案,而是一种将系统划分为独立自治单元,实现水平扩展和故障隔离的架构思想。

互联网业务的高速增长给架构带来了两个根本性挑战:容量的天花板可用性的脆弱性。传统的垂直扩展(Scale-up)终有极限,而简单的水平扩展(Scale-out)在数据一致性、服务依赖、运维复杂度等方面又面临诸多困难。

SET 化架构(也称为单元化架构、Cell-based Architecture)正是为了系统性地解决这些问题而诞生的。本文将从原理到实践,全面解析 SET 化架构的设计与落地。

什么是 SET 化架构?

概念定义

SET(Scalable Elastic Topology,可扩展弹性拓扑)化架构是一种将系统按照某个维度(通常是用户 ID)划分为多个独立、自包含的部署单元的架构模式。每个 SET 都是一个"小型完整系统",拥有独立的应用服务、缓存、数据库等全套基础设施,能够独立处理分配给它的流量。

SET 化的核心思想:

传统架构:         所有用户 → 一套系统
                    (纵向扩展,存在单点瓶颈)

SET 化架构:       用户按规则分组 → 每组对应一个 SET
                    SET-1: 用户 0~999W    → 独立的一套完整系统
                    SET-2: 用户 1000W~1999W → 独立的一套完整系统
                    SET-3: 用户 2000W~2999W → 独立的一套完整系统
                    (水平扩展,理论上无上限)

SET 的核心特征

特征 说明
自包含 每个 SET 拥有完整的服务栈(应用、缓存、DB),能独立处理请求
对等部署 所有 SET 的架构相同,只是处理的数据分片不同
故障隔离 单个 SET 的故障不会影响其他 SET
水平扩展 通过增加 SET 数量实现容量扩展
流量可调度 通过路由规则灵活调度流量在 SET 间的分配

SET 化与传统分布式的区别

维度 传统分布式架构 SET 化架构
扩展方式 各层独立扩展(加应用节点、加 DB 从库) 整体作为一个单元扩展
故障影响 某一层故障影响全局 故障隔离在单个 SET 内
数据分片 数据库层分片,应用层无感知 从入口到数据库全链路分片
部署单元 按服务部署 按 SET(单元)部署
容量规划 各组件独立评估 按 SET 整体评估

SET 化架构演进历程

SET 化不是一步到位的设计,而是随着业务规模增长逐步演化的结果。

阶段一:单体架构

用户 → 应用服务器 → 数据库

所有功能在一个应用中,单库单表。适用于初创期,简单高效。

瓶颈:单机容量有限,数据库成为瓶颈。

阶段二:读写分离 + 缓存

用户 → 应用集群 → 缓存 → 主库(写)/ 从库(读)

通过读写分离缓解数据库压力,引入缓存降低 DB 负载。

瓶颈:写入瓶颈无法解决,主库仍是单点。

阶段三:分库分表

用户 → 应用集群 → 数据库中间件 → DB 分片 1 / DB 分片 2 / DB 分片 N

数据库水平拆分,解决写入瓶颈。但分片逻辑散落在各处,跨分片查询复杂。

瓶颈:应用层无分片感知,缓存与 DB 分片不对齐,运维复杂。

阶段四:服务化(微服务)

用户 → API 网关 → 微服务 A / 微服务 B / ... → 各自的 DB

按业务域拆分为独立服务,各服务独立部署和扩展。

瓶颈:服务间调用复杂,全链路缺乏统一的分片和隔离机制。

阶段五:SET 化(单元化)

用户 → 统一路由层 → SET-1(完整服务栈)/ SET-2 / SET-N
                       ↕ 数据同步

全链路按统一维度分片,每个 SET 自包含完整服务栈,实现真正的水平扩展和故障隔离。

这就是 SET 化架构的终态。 下面详细介绍每个核心组件的设计。

核心设计一:流量路由

流量路由是 SET 化架构的"大脑",它决定了每个请求应该被路由到哪个 SET。

路由键的选择

路由键(Sharding Key)是 SET 化的核心决策之一,选择不当会导致严重的跨 SET 调用问题。

路由键 优点 缺点 适用业务
用户 ID 用户维度天然隔离,覆盖面广 用户间交互需跨 SET 电商、社交、O2O
商户 ID 商户维度隔离 用户下单需跨 SET B 端平台
地理区域 天然的流量隔离 跨区域业务需特殊处理 本地生活、物流
订单 ID 订单维度隔离 需要提前生成带路由信息的 ID 交易系统

实践经验:绝大多数 C 端业务选择用户 ID 作为路由键,因为用户是最核心的业务实体,以用户为维度分片可以最大程度地减少跨 SET 调用。

路由架构设计

SET 化的路由通常分为三层:

第一层:接入路由(DNS / LB 层)

在最外层通过 DNS 或负载均衡器将流量分配到对应的 SET。

用户请求 → DNS 解析 → 全局负载均衡(GSLB)
                            ↓
                    根据用户 ID 哈希路由
                    ↓           ↓           ↓
                 SET-1 LB    SET-2 LB    SET-3 LB

第二层:网关路由(API Gateway 层)

API 网关根据请求中的路由键(如 Header、Cookie、Token 中的用户 ID)将请求路由到正确的 SET。

请求 → API Gateway → 提取路由键 → 查询路由表 → 转发到目标 SET

第三层:服务路由(RPC 层)

服务间调用时,RPC 框架自动根据上下文中的路由键将请求路由到同 SET 的服务实例。

Service A (SET-1) → RPC Framework → 自动路由到 → Service B (SET-1)
                    (通过上下文传递 SET 标识)

路由表设计

路由表是映射用户到 SET 的核心数据结构:

路由表结构:
┌──────────────┬──────────┬──────────┐
│  分片范围      │  SET ID  │  状态     │
├──────────────┼──────────┼──────────┤
│  0 ~ 999      │  SET-1   │  Active  │
│  1000 ~ 1999  │  SET-2   │  Active  │
│  2000 ~ 2999  │  SET-3   │  Active  │
│  3000 ~ 3999  │  SET-1   │  Active  │  ← 同一个 SET 可承载多个分片
└──────────────┴──────────┴──────────┘

路由策略的关键设计要点:

  1. 虚拟分片:不直接将用户映射到物理 SET,而是先映射到虚拟分片(如 1024 个),再将虚拟分片映射到物理 SET。这样扩容时只需调整虚拟分片的映射关系
  2. 路由缓存:路由表在网关和服务端本地缓存,避免每次请求都查询路由服务
  3. 路由一致性:路由表变更时需要保证全链路一致性,避免请求被路由到错误的 SET

核心设计二:数据分片与同步

数据层是 SET 化最复杂的部分,需要解决数据分片、跨 SET 数据访问、数据同步等问题。

数据分类

SET 化架构中的数据按照与路由键的关系分为三类:

数据类型 定义 存储方式 举例
SET 内数据 与路由键强绑定的数据 仅存储在对应 SET 用户订单、用户资产、购物车
全局数据 所有 SET 共享的数据 全局存储 + 各 SET 只读副本 商品信息、配置数据、类目
跨 SET 数据 涉及多个路由键的数据 全局存储或冗余存储 商户维度的聚合数据、排行榜

SET 内数据

SET 内数据遵循"谁的数据谁存储"原则,每个 SET 只处理和存储自己分片内的数据:

SET-1 数据库:只存储 UserID 0~999 的数据
SET-2 数据库:只存储 UserID 1000~1999 的数据

用户 A (ID=500) 下单 → 请求路由到 SET-1 → 订单写入 SET-1 DB
用户 B (ID=1500) 下单 → 请求路由到 SET-2 → 订单写入 SET-2 DB

全局数据

全局数据(如商品信息)需要所有 SET 都能访问,通常采用以下方案:

方案 原理 优点 缺点
全局服务 独立部署的全局服务 + 数据库 数据一致性好 全局服务成为依赖瓶颈
数据广播 写入全局库后异步同步到各 SET 本地读取性能好 数据有延迟,存储冗余
缓存分发 全局数据写入后推送到各 SET 缓存 读取极快 缓存一致性需要保障

实践建议:高频读取的全局数据(如商品详情)采用"数据广播 + 本地缓存"方案;低频但要求强一致的全局数据(如配置变更)采用"全局服务"方案。

数据同步机制

SET 间的数据同步是保证业务连续性的关键,特别是在故障切换场景下:

                     主 SET                          备 SET
                 ┌──────────┐                    ┌──────────┐
                 │  应用层    │                    │  应用层    │
                 │  缓存层    │                    │  缓存层    │
                 │  数据库    │ ── Binlog 同步 ──→ │  数据库    │
                 └──────────┘                    └──────────┘

        同步方式:MySQL Binlog → Canal/DTS → 目标 SET 数据库
        同步延迟:通常 < 1s,需要监控告警

数据同步的关键指标:

指标 目标值 监控方式
同步延迟 < 1 秒 Binlog 位点差监控
数据一致性 99.99% 定期全量对账
同步可用性 99.99% 同步链路健康检查

核心设计三:全局服务

有些服务天然不能被 SET 化,它们需要作为全局服务为所有 SET 提供能力。

全局 ID 生成

在 SET 化架构中,ID 生成必须保证全局唯一且带有路由信息:

ID 结构设计:
┌────────────┬──────────┬───────────┬──────────┐
│  时间戳      │  SET ID  │  机器 ID   │  序列号   │
│  41 bits    │  5 bits  │  5 bits   │  12 bits │
└────────────┴──────────┴───────────┴──────────┘

总长度:63 bits(Long 类型)
生成方案 优点 缺点 适用场景
全局 ID 服务 全局唯一性保证最强 依赖外部服务,存在可用性风险 核心业务(订单、支付)
本地 Snowflake 无外部依赖,性能最高 需要解决时钟回拨问题 非核心业务
号段模式 批量获取减少调用 号段用尽时有短暂延迟 通用场景

兜底策略:本地 ID 生成作为兜底方案,当全局 ID 服务不可用时自动降级为本地生成,确保业务不中断。

全局配置中心

配置中心负责管理所有 SET 的路由规则、业务配置和开关:

配置中心架构:
                  ┌─────────────────┐
                  │   配置中心集群     │
                  │  (ZK/Nacos/etcd) │
                  └────────┬────────┘
                     ↙     ↓     ↘
            SET-1 Agent  SET-2 Agent  SET-3 Agent
               ↓            ↓            ↓
            本地缓存      本地缓存      本地缓存

推送机制:配置变更 → 配置中心 → 推送给各 SET Agent → 更新本地缓存

全局调度中心

负责 SET 的健康监控、故障检测和流量调度:

功能 说明
健康检查 定期探测各 SET 的健康状态
故障检测 发现 SET 异常时触发告警
流量切换 故障 SET 的流量自动切换到备用 SET
容量管理 监控各 SET 的容量使用率
扩缩容编排 新增或下线 SET 时的流量编排

核心设计四:故障隔离与切换

故障隔离是 SET 化架构最核心的价值之一。

故障域划分

SET 化架构将故障影响范围从"全站"缩小到"单个 SET":

传统架构故障:
  DB 主库宕机 → 全站不可用 → 影响 100% 用户

SET 化架构故障:
  SET-2 DB 宕机 → 仅 SET-2 不可用 → 影响约 33% 用户(假设 3 个 SET)
                    ↓ 自动切换
                 SET-2 流量切换到备用 → 影响时间 < 分钟级

故障切换策略

策略 切换速度 数据风险 适用场景
主备切换 秒级~分钟级 可能丢失未同步数据 SET 内部 DB 主备切换
SET 间切换 分钟级 依赖数据同步延迟 整个 SET 故障
跨机房切换 分钟级~小时级 需要全量数据同步 机房级故障

故障切换流程

正常状态:
  用户流量 → 路由层 → SET-2(主)

故障检测:
  健康检查失败 → 确认 SET-2 不可用 → 触发切换流程

切换执行:
  1. 停止 SET-2 的流量接入(路由层摘除)
  2. 等待 SET-2 → SET-2-备 的数据同步完成(或接受部分数据丢失)
  3. 更新路由表:SET-2 的分片 → SET-2-备
  4. 开放 SET-2-备 的流量接入
  5. 验证切换后的业务正确性

恢复状态:
  用户流量 → 路由层 → SET-2-备(新主)

容灾等级

等级 容灾范围 实现方式 RTO
L1 单机故障 应用集群 + DB 主备 秒级
L2 机架故障 跨机架部署 秒级
L3 机房故障 同城双机房 SET 互备 分钟级
L4 城市故障 异地 SET 互备 分钟级~小时级

核心设计五:SET 扩缩容

SET 化架构的一个重要优势是可以通过增减 SET 数量来调整系统容量。

扩容流程

扩容场景:当前 3 个 SET 容量不足,需要扩容到 4 个 SET

Step 1: 部署新 SET(SET-4)
  - 部署完整的应用服务、缓存、数据库
  - 从现有 SET 同步全局数据

Step 2: 数据迁移
  - 将 SET-1 的部分虚拟分片的数据迁移到 SET-4
  - 采用双写方案保证迁移过程不中断服务

Step 3: 路由切换
  - 更新路由表:迁移的虚拟分片指向 SET-4
  - 灰度切换流量,逐步验证

Step 4: 清理
  - 验证完成后,清理 SET-1 中已迁移的数据
  - 回收空闲资源

虚拟分片的价值

虚拟分片是实现平滑扩缩容的关键:

初始状态(3 个 SET,1024 个虚拟分片):
  SET-1: 虚拟分片 0~341
  SET-2: 虚拟分片 342~682
  SET-3: 虚拟分片 683~1023

扩容到 4 个 SET(只需调整虚拟分片映射):
  SET-1: 虚拟分片 0~255
  SET-2: 虚拟分片 256~511
  SET-3: 虚拟分片 512~767
  SET-4: 虚拟分片 768~1023

优势:用户 → 虚拟分片的映射不变,只调整虚拟分片 → 物理 SET 的映射

实践案例:电商交易系统 SET 化

以一个典型的电商交易系统为例,展示 SET 化的具体落地方案。

业务分析

服务 路由键关系 SET 化策略
用户服务 用户 ID(强绑定) SET 内部署
订单服务 用户 ID(强绑定) SET 内部署
支付服务 用户 ID(强绑定) SET 内部署
商品服务 无关(全局数据) 全局部署 + 数据广播
库存服务 商品维度(跨 SET) 全局部署
搜索服务 无关(全局数据) 全局部署
营销服务 活动维度(跨 SET) 全局部署

整体架构

                        ┌──────────────────────────────────┐
                        │          统一接入层(GSLB)         │
                        └───────────────┬──────────────────┘
                                        ↓
                        ┌──────────────────────────────────┐
                        │         API Gateway(路由层)       │
                        │    提取 UserID → 查询路由表 → 转发   │
                        └──┬──────────────┬────────────┬───┘
                           ↓              ↓            ↓
                    ┌─────────────┐ ┌─────────────┐ ┌─────────────┐
                    │   SET-1     │ │   SET-2     │ │   SET-3     │
                    │ ┌─────────┐ │ │ ┌─────────┐ │ │ ┌─────────┐ │
                    │ │用户服务  │ │ │ │用户服务  │ │ │ │用户服务  │ │
                    │ │订单服务  │ │ │ │订单服务  │ │ │ │订单服务  │ │
                    │ │支付服务  │ │ │ │支付服务  │ │ │ │支付服务  │ │
                    │ │Redis    │ │ │ │Redis    │ │ │ │Redis    │ │
                    │ │MySQL    │ │ │ │MySQL    │ │ │ │MySQL    │ │
                    │ └─────────┘ │ │ └─────────┘ │ │ └─────────┘ │
                    └─────────────┘ └─────────────┘ └─────────────┘
                           ↕              ↕            ↕
                    ┌──────────────────────────────────────────┐
                    │              全局服务层                     │
                    │  商品服务 │ 库存服务 │ 搜索服务 │ 营销服务    │
                    │         全局 ID 服务 │ 配置中心              │
                    └──────────────────────────────────────────┘

下单流程的 SET 化处理

用户 A(ID=500)下单购买商品 X:

1. 请求到达 API Gateway
2. Gateway 提取 UserID=500,查路由表 → SET-1
3. 请求转发到 SET-1 的订单服务
4. 订单服务调用全局商品服务查询商品信息
5. 订单服务调用全局库存服务扣减库存
6. 订单服务在 SET-1 本地 DB 创建订单
7. 订单服务调用 SET-1 本地的支付服务发起支付
8. 支付完成后,SET-1 的订单服务更新本地订单状态

关键点:

  • 用户维度的数据操作(创建订单、支付)在 SET 内完成,无跨 SET 调用
  • 商品、库存等全局数据通过全局服务访问
  • RPC 框架自动将 SET 标识通过上下文传递,保证 SET 内调用的正确性

SET 化实施路线

SET 化是一个渐进式的过程,不应该一步到位。

阶段规划

阶段 目标 关键动作 周期
P0:基础设施准备 具备 SET 化的基础能力 统一 RPC 框架、引入路由组件、改造 ID 生成 1~2 月
P1:核心链路 SET 化 交易核心链路实现 SET 化 订单、支付、用户服务 SET 化部署 2~3 月
P2:全链路 SET 化 所有服务完成 SET 化改造 非核心服务 SET 化、全局服务治理 3~6 月
P3:异地 SET 实现异地多活能力 跨机房 SET 部署、数据同步、故障切换 3~6 月

改造清单

应用层改造

  • 所有服务支持从请求上下文中提取和传递路由键
  • RPC 框架支持基于路由键的服务路由
  • 消息队列的生产和消费支持 SET 路由
  • 定时任务支持按 SET 分片执行

数据层改造

  • 数据库按 SET 进行物理隔离
  • 缓存按 SET 进行 namespace 隔离
  • 全局数据的同步机制建设
  • 数据对账和修复工具

基础设施改造

  • 统一路由服务建设
  • 全局 ID 生成服务建设
  • 监控体系支持 SET 维度
  • 发布系统支持按 SET 灰度

SET 化与异地多活的关系

SET 化架构是异地多活的基础。两者的关系可以这样理解:

SET 化 = 单元化部署 + 流量路由 + 数据分片
异地多活 = SET 化 + 跨地域部署 + 数据同步 + 故障切换
维度 同城 SET 化 异地多活 SET 化
部署范围 同城多机房 跨城市多机房
网络延迟 < 1ms 10~50ms
数据同步 同步/半同步复制 异步复制(最终一致性)
故障切换 自动秒级切换 手动/半自动分钟级切换
核心挑战 路由准确性 数据一致性 + 切换决策

SET 化架构天然具备"每个 SET 独立自治"的特性,这为异地多活提供了完美的基础。只需将不同的 SET 部署到不同的地域,配合数据同步和流量调度,就能实现异地多活。

常见问题与解决方案

跨 SET 调用问题

问题:部分业务场景不可避免需要跨 SET 访问数据。

解决方案

场景 解决方案
用户查看商户信息 商户数据作为全局数据广播
商户查看所有订单 聚合服务从各 SET 并行查询后合并
全站排行榜 各 SET 本地计算后汇总到全局服务
跨用户转账 通过消息队列异步通知目标 SET

数据迁移问题

问题:扩容时需要在 SET 间迁移数据。

解决方案:双写方案

Phase 1: 新 SET 开始从旧 SET 同步增量数据(Binlog 订阅)
Phase 2: 同步追上后,开启双写模式(新请求同时写入新旧 SET)
Phase 3: 路由切换,新请求全部路由到新 SET
Phase 4: 验证无误后,停止双写,清理旧数据

全局服务瓶颈

问题:全局服务成为所有 SET 的共同依赖,可能成为瓶颈。

解决方案

  1. 数据本地化:全局数据尽可能广播到各 SET 本地,减少全局服务调用
  2. 缓存优先:全局数据走多级缓存,降低对全局 DB 的访问
  3. 异步化:非实时性要求的全局操作通过消息队列异步处理
  4. 弹性扩展:全局服务本身也需要集群化部署和弹性扩展

总结

SET 化架构是应对互联网业务规模化增长的系统性解决方案。它的核心思想并不复杂——把一个大系统拆分成多个独立自治的小系统——但真正的挑战在于落地过程中的每一个细节。

回顾 SET 化的关键设计决策:

  1. 路由键选择决定了架构的天花板。选错路由键会导致大量跨 SET 调用,抵消 SET 化的优势
  2. 数据分类是 SET 化的基础。明确哪些是 SET 内数据、哪些是全局数据,才能设计合理的数据架构
  3. 虚拟分片是弹性扩展的关键。不要将用户直接映射到物理 SET,虚拟分片层带来的灵活性至关重要
  4. 全局服务的治理不能忽视。全局服务是所有 SET 的共同依赖,必须做到高可用和高性能
  5. 渐进式实施是务实的选择。从核心链路开始,逐步扩展,而不是试图一步到位

SET 化不是目的,而是手段。 它服务于两个根本目标:让系统能够水平扩展以承载业务增长,让故障影响可控以保障用户体验。在实施 SET 化之前,先问自己:当前的业务规模真的需要 SET 化吗?

加载导航中...

评论