这是 Beta 探索课程,内容结构、实验步骤和示例可能会继续调整。
完整架构
基于前面的学习,让我们设计一个生产级的分布式 ID 系统完整架构。
1. 整体架构
┌─────────────────┐
│ 负载均衡器 │
│ (Nginx / SLB) │
└────────┬────────┘
│
┌───────────────────┼───────────────────┐
│ │ │
┌────▼────┐ ┌────▼────┐ ┌────▼────┐
│ 节点 A │ │ 节点 B │ │ 节点 C │
│号段模式 │ │号段模式 │ │号段模式 │
└────┬────┘ └────┬────┘ └────┬────┘
│ │ │
└───────────────────┼───────────────────┘
│
┌───────────────────┼───────────────────┐
│ │ │
┌────▼────┐ ┌────▼────┐ ┌────▼────┐
│ DB 主库 │ │ DB 从库 │ │ Redis │
└─────────┘ └─────────┘ └─────────┘
2. 分层设计
接入层:
功能:
- 负载均衡
- 流量控制
- 服务发现
- 健康检查
服务层:
功能:
- ID 生成逻辑
- 号段管理
- 双 Buffer
- 降级策略
数据层:
功能:
- 数据库存储
- Redis 缓存
- 数据持久化
1. 号段服务
职责:
- 管理号段分配
- 维护双 Buffer
- 处理数据库交互
实现:
2. 双 Buffer
职责:
- 维护两个号段 Buffer
- 自动切换
- 预加载
实现:
3. 降级服务
职责:
- 主方案失败时降级
- 提供多级降级
- 监控降级状态
实现:
1. 多机房部署
北京机房:
├── 节点 A1、A2、A3
├── DB 主库
└── Redis Cluster
上海机房:
├── 节点 B1、B2、B3
├── DB 从库
└── Redis Cluster
深圳机房:
├── 节点 C1、C2、C3
├── DB 从库
└── Redis Cluster
2. 故障转移
流程:
1. 健康检查发现故障
2. 流量切换到健康节点
3. 故障节点自动重启
4. 重新加入集群
1. 监控指标
QPS:每秒生成的 ID 数量
延迟:P50, P99, P999
错误率:生成 ID 失败的比例
可用性:服务可用时间比例
资源使用:CPU、内存、网络
号段使用率:Buffer 使用情况
2. 告警规则
QPS < 阈值 → P3 告警
延迟 P99 > 阈值 → P2 告警
错误率 > 阈值 → P1 告警
可用性 < 阈值 → P0 告警
1. 本地缓存
服务层拿到一批号段之后,先缓存在本地内存里再逐个发放。业务线程只和内存打交道,号段不够了才去数据库取下一段。
业务线程 → 本地内存(原子自增)→ 发放 ID
│ 剩余量见底
↓
向号段服务申请下一段
本地缓存里要放三样东西:号段的左右端点、当前待发位置、续期阈值。缺一样就会出现「发空」或者「重复发号」。
| 观测指标 | 含义 | 处置建议 |
|---|---|---|
| 缓存剩余量 | 当前号段还剩多少 | 长期低于 10% 说明步长偏小或流量超出预期 |
| 续期耗时 | 从发现不足到新号段就绪 | 明显变长说明数据库压力上来了 |
| 续期失败次数 | 后台预取失败的次数 | 不为 0 就必须告警,它不会体现在业务错误率上 |
一个号段用完之后,下一段要提前取回来(这就是双 Buffer),否则切换时的等待会变成一次实实在在的接口卡顿。
2. 批量生成
批量生成是接口层的批量能力:一次请求要 100 个 ID 时,不该发 100 次请求,也不该查 100 次数据库。
一次请求 100 个 ID:
- 逐个生成:100 次业务调用 + 100 次号段服务调用
- 批量生成:1 次业务调用 + 1 次号段服务调用
实现:
/**
* 批量取 ID 的接口实现。
*/
@RestController
@RequestMapping("/api/id")
public class IdController {
private final SegmentIdGenerator idGenerator;
public IdController(SegmentIdGenerator idGenerator) {
this.idGenerator = idGenerator;
}
/**
* 批量获取 ID。
*
* count 限制在 1 到 1000 之间,防止单个请求把整个号段领空。
*/
@GetMapping("/batch")
public List<Long> batchIds(@RequestParam(defaultValue = "1") int count) {
if (count < 1 || count > 1000) {
throw new IllegalArgumentException("count 必须在 1 到 1000 之间");
}
List<Long> ids = new ArrayList<>(count);
for (int i = 0; i < count; i++) {
ids.add(idGenerator.nextId());
}
return ids;
}
}
批量生成有三条纪律:
- 批量大小要有上限。1000 是常见取值,再大就相当于让一个请求领走整个号段,一旦这个请求重试,浪费的号段会非常大。
- 批量接口要限流。批量取号天然是「一次调用消耗多个 ID」,不做限流等于给了一个放大器。
- 批量接口要能被降级。号段服务不可用时可以降级成雪花算法或者 UUID,但要接受有序性下降的代价。
1. Docker 部署
部署要点
- 镜像只负责提供稳定运行环境,业务可靠性仍然取决于状态、监控和回滚策略。
2. Kubernetes 部署
配置要点
- 配置表达的是环境差异和运行参数,不是业务规则本身。
1. 选择合适方案
小型系统:Redis 自增
中型系统:雪花算法
大型系统:号段模式
超大系统:号段模式 + 双 Buffer
2. 多级降级
主方案:号段模式
降级方案1:雪花算法
降级方案2:UUID
降级方案3:时间戳 + 随机数
3. 完善监控
实时监控
告警机制
日志追踪
性能分析
完整架构特点
| 特性 | 说明 |
|---|---|
| 唯一性 | 数据库保证 |
| 有序性 | 严格递增 |
| 性能 | > 10,000,000 QPS |
| 可用性 | 99.99% |
| 扩展性 | 水平扩展 |
| 降级 | 多级降级 |
课程总结
恭喜你完成了《系统设计 - 分布式 ID 生成器》课程!
你学到了: ✅ 分布式 ID 的多种方案 ✅ 各方案的优缺点和适用场景 ✅ 生产级系统的设计原则 ✅ 完整的系统架构
下一步
建议继续学习:
- 系统设计其他主题
- 分布式系统设计
- 微服务架构
- 高可用系统设计
感谢学习!🎉
发现内容有问题?
告诉我们哪里需要改进。
