题干与适用场景
你负责一个全球 SaaS,单一服务集群中的数据库、队列或发布故障可能影响全部租户。请设计 cell-based architecture:每个 cell 是可独立运行的完整系统副本,服务固定的一组租户;入口根据稳定映射把请求送到目标 cell。
回答需要覆盖租户分片、路由目录、每 cell 的计算与数据边界、跨 cell 报表、发布、迁移、容量和灾难恢复。AWS 的 cell guidance 将 cell 描述为独立副本,并把 user-to-cell mapping 放在高可用存储中;bulkhead 指南强调路由层应按 partition key 分发请求并保持单一入口。
面试官考察点
- 是否先定义故障影响范围、租户一致性和可接受的降级。
- 是否把 cell 设计成真正独立的故障域,而不是只复制无状态服务。
- 是否识别路由目录、控制面、共享依赖和跨 cell 查询的新故障点。
- 是否说明新增 cell、重平衡和租户迁移的可回滚步骤。
- 是否用容量、错误率和跨 cell 依赖指标验证 blast radius。
系统设计面试资料把 cell-based architecture 归为大规模系统的隔离模式。强回答会解释它何时值得承担重复基础设施和运维复杂度,而不是把 cell 当成微服务的同义词。
回答前需要澄清的问题
- 故障目标是单租户、一个 cell、一个可用区还是整个区域?
- 租户之间是否允许共享数据、全局搜索或跨租户聚合?
- 哪些操作必须线性一致,哪些报表可以延迟或最终一致?
- 迁移期间是否允许短暂只读?是否有合规的地域驻留约束?
- 目标可用性、租户数量、增长速度、单 cell 容量和发布频率是多少?
30 秒回答框架
我会先按稳定租户键把流量分配到固定 cell,每个 cell 拥有独立的计算、队列、缓存和主数据存储;全局控制面只维护版本、容量和映射。入口路由读取高可用目录,故障时只摘除受影响 cell。跨 cell 报表走异步汇总,不让查询重新引入共享数据库。新增 cell、迁移和发布都采用小批量、可观测、可回滚的流程,并用 cell 级 SLO 验证故障边界。
分步骤深入解答
第一步:定义 cell 边界与故障假设
把一个 cell 定义为可独立部署和恢复的完整副本:API、工作队列、缓存、数据库、对象存储前缀和监控。共享的身份、计费或配置服务要明确可接受的故障范围;如果共享控制面挂掉会阻断所有 cell,它就不是数据面完全隔离。
第二步:选择分片键与路由目录
以 tenant_id 作为稳定 partition key,目录记录 tenant 到 cell、版本、迁移状态和容量。路由层先从本地缓存读取,再从高可用目录刷新;更新采用版本号和租约,防止迁移时旧路由把写请求送到两个 cell。客户端只看到一个域名,路由层负责重试边界。
第三步:建立 cell 内数据平面
每个 cell 有独立主库和消息队列,租户数据不跨 cell 直接写。读副本、缓存和对象存储按 cell 命名,备份也保留 cell 元数据。需要全局配置时使用只读快照或版本化发布,禁止把高频请求回退到一个全球共享数据库。
第四步:处理跨 cell 查询
产品报表通过每个 cell 的变更流生成汇总表,再由全局分析层读取。查询必须标记数据时间和遗漏 cell,不能假设实时完整。需要强一致的跨租户操作应缩小范围、改成异步工作流,或明确它会牺牲 cell 隔离;不要在请求路径上做跨 cell 两阶段提交。
第五步:设计故障与降级路径
健康检查分为 cell 内依赖、路由可达性和业务正确性。某 cell 失败时,路由目录把它标记为 draining,新请求停止进入;只读缓存或异步任务可按业务允许降级。不要把租户自动漂移到任意 cell,除非数据复制、幂等和权限边界已验证,否则故障转移会制造重复写入。
第六步:新增 cell 与容量重平衡
以基础设施模板创建空 cell,先运行合成流量和影子读,再接入少量租户。容量指标包括 CPU、数据库连接、队列延迟、存储增长和每租户成本。重平衡先冻结映射版本,复制数据并校验计数,短暂切换写入,再观察新旧 cell 的一致性,失败则回退映射而不是删除旧数据。
第七步:发布与版本治理
发布控制面、cell 模板和业务版本分开。先在一个 cell 做 canary,再按 cell 扩大;路由目录保留兼容窗口,避免新版本写入旧版本无法读取的字段。跨 cell 的版本分布和错误率必须可见,不能因为全局平均成功率正常就忽略一个坏版本。
第八步:验证 blast radius 与运营成本
故障演练应注入单 cell 数据库、队列、路由目录和发布错误,确认影响只覆盖预期租户。指标包括每 cell 可用性、错误预算、跨 cell 请求比例、迁移回滚时间、共享控制面依赖和容量余量。若 cell 数量太小无法隔离,或重复基础设施成本超过可靠性收益,应回到分区或 bulkhead 的更简单方案。
高质量示范回答
我会把 tenant_id 映射到固定 cell,每个 cell 独立运行 API、队列、缓存和主数据存储,控制面只维护版本、容量和映射。入口路由以单一域名提供服务,读取带版本的高可用目录;故障 cell 进入 draining,避免把未验证的数据写入其他 cell。跨 cell 报表通过变更流异步汇总,迁移采用复制、校验、短暂切换和可回滚映射。用 cell 级 SLO、共享依赖占比、迁移回滚时间和故障演练证明 blast radius;当隔离收益小于复制和运维成本时,选择更简单的 bulkhead。
常见错误
- 只复制无状态服务 → 共享数据库仍是单点 → 画清每个 cell 的数据和队列边界。
- 让租户故障时随机漂移 → 产生重复写入或权限错配 → 先验证复制、幂等和路由版本。
- 在请求路径跨 cell 聚合 → 新的全球故障域 → 用异步汇总和数据时间标记。
- 只有全局健康检查 → 坏 cell 被平均值掩盖 → 记录 cell 级 SLO 和版本分布。
- 迁移直接改路由 → 新旧写入并存 → 使用映射版本、复制校验和可回滚切换。
- 每个依赖都做独立 cell → 成本和运维失控 → 先量化 blast radius 与容量收益。
追问及应对
路由目录挂了怎么办?
保留带版本的本地缓存和只读快照,限制映射变更,并让已有租户继续访问原 cell。目录恢复前不做大规模重平衡。
跨 cell 的管理员报表必须实时怎么办?
先确认“实时”允许的延迟和缺失语义。若必须强一致,就缩小操作范围或接受更大的共享故障域;常规报表应使用带时间戳的异步汇总。
单个 cell 容量不足如何迁移?
先复制并校验数据,发布新映射版本,短暂协调写入,再逐步放量。迁移期间保留旧 cell,发现差异时回滚映射。
一个版本只在部分 cell 上线安全吗?
可以,但必须有兼容 schema、版本可观测性和明确的放量顺序。全局成功率不能替代逐 cell 的错误预算。
如何证明故障没有扩散?
演练 cell 内数据库、队列、路由和发布故障,记录受影响租户集合、跨 cell 请求、恢复时间和共享依赖调用。
什么时候不用 cell-based architecture?
租户量小、故障成本低或数据强一致跨租户操作占主导时,cell 的重复资源和迁移复杂度可能不值得。
cell 之间如何共享身份与计费?
把共享服务限制在低频控制面,缓存只读结果并定义降级;计费写入要有幂等键和补偿流程,避免共享服务故障扩散到所有数据面。