核心洞察
为 1,000 用户构建系统很容易。保持快速、便宜、坚如磐石地支撑 100 万并发用户是大多数工程师失败的地方。
1. 需求定义(大多数工程师跳过这步)
为什么跳过
六个月后重建一切。
非功能性需求
| 指标 | 目标 |
|---|---|
| 峰值 QPS | 10k-50k 请求/秒 |
| P99 延迟 | <200ms(目标 <100ms) |
| 正常运行时间 | 99.99%(每月最多 4 分钟停机) |
| 一致性模型 | 财务数据强一致,feeds/timelines 最终一致 |
| 成本上限 | 每 1,000 用户设定严格金额 |
Scale Cube 三维思考
| 轴 | 策略 | 说明 |
|---|---|---|
| X 轴 | 水平复制 | 运行更多相同服务器 |
| Y 轴 | 功能分解 | 拆分为更小、专注的服务 |
| Z 轴 | 数据分区 | 按用户 ID 或地区分片 |
这个思维模型 alone 防止 90% 的扩展灾难。
2. 基础选型
语言和框架
| 场景 | 选择 | 理由 |
|---|---|---|
| 原始性能 | Go / Rust | 内存效率 |
| 团队速度优先 | TypeScript (NestJS) / Java/Kotlin (Spring Boot) | 开发效率 |
API 策略
| 类型 | 协议 | 特点 |
|---|---|---|
| 对外 | REST + GraphQL | 通用 |
| 内部服务间 | gRPC | 类型化、快速、支持流 |
架构起点
从模块化单体开始。
仅在特定服务成为可测量、已证实的瓶颈时才提取微服务。
过早微服务是扩展时工程速度的最大杀手。
3. 负载均衡和边缘层
全球用户 = 全球延迟
除非将基础设施推到边缘。
请求路径
用户(Lagos / London / Mumbai)
↓
Cloudflare / Fastly:CDN、DDoS 防护、边缘缓存
↓
全球负载均衡器:AWS Global Accelerator 或 GCP 等效
↓
API 网关:限流、WAF、机器人检测
↓
应用服务
在 100 万用户时,边缘层是第一道防线。
尽可能在请求到达应用服务器前处理流量。
4. 应用层设计
四条不可妥协的规则
规则 1:每个服务必须无状态
- 会话属于 Redis 或 Dragonfly
- 绝不在应用内存中
- 如果服务器死亡,没有用户会话应该随之死亡
规则 2:后台任务属于队列
| 规模 | 选择 | 避免 |
|---|---|---|
| 此规模 | Kafka / Pulsar | RabbitMQ、内存处理、setTimeout |
规则 3:调用级韧性
- 断路器
- 带抖动的重试
- 每个出站调用的硬超时
一个慢依赖不应该级联为完整系统故障。
规则 4:按正确信号自动扩展
- CPU 是滞后指标
- 按队列深度和错误率扩展
- 这些在实际系统压力变得在 CPU 数字中可见之前反映压力
5. 数据库策略
核心原则
单一数据库既是性能天花板也是单点故障。绝不只用一个。
推荐栈
| 用途 | 技术 |
|---|---|
| 主 OLTP | PostgreSQL + Citus 水平分片 |
| 高写入路径 | Cassandra / ScyllaDB |
| 缓存和实时数据 | Redis Cluster |
| 分析和聚合 | ClickHouse |
架构
- 所有写入通过查询路由器、Vitess 或 Citus
- 按 user_id 使用取模策略分片,数据均匀分布
- 读副本在 PgBouncer 后做连接池
不可过度强调的规则
第一天就选择你的分片键。
数据进入生产后更改它是工程团队能经历的最痛苦的迁移之一。
6. 多层缓存
缓存是最大成本杠杆
目标缓存命中率:>85%。
三层架构
| 层级 | 内容 | 说明 |
|---|---|---|
| Layer 1 | 边缘 CDN 缓存 | 静态资产和公开可缓存响应 never reach origin |
| Layer 2 | 应用级 Redis 缓存 | 计算结果、会话数据、热记录 |
| Layer 3 | 查询结果缓存 | 昂贵数据库读 |
仅全缓存未命中时请求才到达数据库。
失效策略
- 使用 Kafka 事件驱动失效
- 用户资料更新事件应立即失效相关缓存 key
- TTL alone 对关键数据不可靠
7. 可观测性
核心原则
无法调试看不见的东西。
需要完整可观测性从第一天开始,不是凌晨 2 点生产故障后。
技术栈
| 组件 | 用途 |
|---|---|
| Prometheus | 指标 |
| Jaeger | 分布式追踪 |
| Loki | 日志 |
| OpenTelemetry | 全部接入 |
| Grafana | 仪表盘 |
| Alertmanager | 路由关键警报到 PagerDuty |
四个黄金信号
| 信号 | 说明 |
|---|---|
| 延迟 | 请求耗时 |
| 流量 | 请求数量 |
| 错误 | 失败请求率 |
| 饱和度 | 资源接近极限程度 |
关键实践
- 定义真实 SLO
- 追踪错误预算
- 在用户注意到前告警
8. 数据一致性和幂等性
核心原则
分布式系统以部分、不可预测的方式失败。从一开始就为此设计。
Outbox 模式
- 事件写入数据库表
- 与业务逻辑同一事务
- 后台进程读取并发布
- 事件永不丢失,即使消息代理宕机
Saga 模式
- 跨多服务事务
- 用补偿动作序列而非两阶段提交
- 每步有定义的下游失败回滚
幂等键
- 每个变更操作接受唯一请求 ID
- 同一请求两次到达 → 返回原始结果
- 处理不可靠网络上重试的唯一安全方式
最终一致性
在这个规模不是弱点。是正确的架构权衡。
9. 安全
| 措施 | 说明 |
|---|---|
| mTLS | 所有内部服务间,每次服务间调用认证加密 |
| 零信任网络 | 默认不信任任何服务,无论请求来源 |
| 加密 | 静态和传输中 everywhere,无例外 |
| 混沌工程 | 定期故意在生产中杀死随机 pod,验证韧性假设 |
核心原则:
在受控测试中发现失败模式比凌晨 2 点流量高峰时发现好 infinite 倍。
10. 完整架构
1 Million Users
↓
Edge + CDN + WAF
↓
Global Load Balancer
↓
API Gateway (Rate Limiting + Auth)
↓
Stateless Application Services
↓
Redis Cluster / Kafka Event Bus / Sharded PostgreSQL + Cassandra
↓
Background Workers
↓
Observability Layer: Prometheus, Grafana, Jaeger, Loki
支撑 100 万日活用户,并有显著余量扩展到数千万,无需根本重写。
启动前检查清单
| 检查项 | 说明 |
|---|---|
| 从小开始并测量一切 | 基于数据优化,不是假设 |
| 负载测试 | 用 k6 或 Locust 在 2 倍预期峰值流量下 |
| 功能标志和金丝雀部署 | 绝不直接推送给 100% 用户 |
| 分析热路径 | 优化前,瓶颈很少在你想的地方 |
| 使用 spot/preemptible 实例 | 非关键工作负载,显著削减基础设施成本 |
核心结论
差异来源
不是天赋。
是早期做出的架构决策,在流量到达之前。
资源
- 作者:Akintola Steve (@Akintola_steve)
- 原文:https://x.com/Akintola_steve/status/2055620856802357587
- 延伸阅读:
- Designing Data-Intensive Applications (Martin Kleppmann)
- Uber Engineering Blog
- Netflix Tech Blog
- ByteByteGo