这是 Beta 探索课程,内容结构、实验步骤和示例可能会继续调整。
性能要求
高性能是分布式 ID 生成器的关键指标之一。在高并发场景下,ID 生成不能成为系统的性能瓶颈。
性能指标
1. QPS(每秒查询数)
定义:每秒钟能生成的 ID 数量。
不同场景的 QPS 要求:
小型系统:< 10,000 QPS
中型系统:10,000 - 100,000 QPS
大型系统:100,000 - 1,000,000 QPS
超大型系统:> 1,000,000 QPS真实案例:
淘宝双11:峰值 > 1,000,000 QPS
微信消息:峰值 > 10,000,000 QPS
抖音视频:峰值 > 50,000,000 QPS2. 延迟(Latency)
定义:从请求 ID 到获得 ID 的时间。
延迟指标:
P50:50% 的请求延迟
P99:99% 的请求延迟
P999:99.9% 的请求延迟延迟要求:
优秀:< 1ms
良好:1 - 10ms
可接受:10 - 100ms
不可接受:> 100ms影响延迟的因素:
- 网络往返时间(RTT)
- 数据库查询时间
- 锁竞争
- 系统负载
3. 吞吐量(Throughput)
定义:单位时间内处理的总请求数。
吞吐量计算:
吞吐量 = QPS × 平均响应时间
示例:
QPS = 100,000
平均响应时间 = 0.5ms
吞吐量 = 50,000 requests/ms4. 并发度(Concurrency)
定义:同时处理的请求数量。
并发度计算:
并发度 = QPS × 延迟
示例:
QPS = 100,000
延迟 = 1ms
并发度 = 100性能瓶颈分析
1. 网络瓶颈
问题:每次生成 ID 都需要网络调用。
影响:
网络往返时间:RTT ≈ 1-10ms
延迟增加:1-10ms
QPS 限制:受网络带宽限制解决方案:
- 本地生成 ID
- 批量获取 ID
- 缓存 ID 段
2. 数据库瓶颈
问题:基于数据库的 ID 生成受限于数据库性能。
影响:
单机 MySQL QPS:约 5,000 - 50,000
数据库成为瓶颈
连接池耗尽解决方案:
- 使用缓存
- 批量获取号段
- 多数据库实例
- 使用 Redis
3. 锁竞争瓶颈
问题:分布式锁或互斥锁导致性能下降。
影响:
等待锁的时间:0-100ms
并发度受限
吞吐量下降解决方案:
- 无锁设计
- 分段锁
- 本地生成
- 乐观锁
4. 时钟同步瓶颈
问题:基于时间戳的方案需要时钟同步。
影响:
时钟同步开销:系统调用
时钟精度限制:系统时钟精度解决方案:
- 使用缓存时钟
- 使用原子时钟
- 使用序列号补充
性能优化方法
1. 本地生成
原理:在本地生成 ID,避免网络调用。
实现:
// 本地递增
private static long counter = 0;
public long generateId() {
return ++counter;
}性能:
QPS:> 1,000,000
延迟:< 0.001ms2. 批量获取
原理:一次获取一批 ID,本地分配。
实现:
// 从数据库获取号段
SELECT id FROM id_segment
WHERE type = 'order'
FOR UPDATE;
UPDATE id_segment
SET current_id = current_id + 1000
WHERE type = 'order';
// 本地分配 1000 个 ID
for (int i = 0; i < 1000; i++) {
yield nextId++;
}性能提升:
原来:每次请求都访问数据库
现在:1000 次请求才访问数据库一次
性能提升:1000x3. 双 Buffer 机制
原理:使用两个 Buffer,一个当前使用,一个预加载。
实现:
Buffer1:[1000, 2000] ← 当前使用
Buffer2:[2000, 3000] ← 预加载
当 Buffer1 用完:
- 切换到 Buffer2
- 异步预加载 Buffer3
优势:
- 始终有可用 ID
- 零等待4. 无锁设计
原理:避免使用锁,使用原子操作。
实现:
性能对比:
有锁:10,000 QPS
无锁:100,000 QPS
性能提升:10x5. 预分配号段
原理:预先为每个节点分配独立的 ID 号段。
实现:
节点1:1000000 - 1999999
节点2:2000000 - 2999999
节点3:3000000 - 3999999
每个节点独立生成,无需协调6. 缓存优化
原理:使用缓存减少数据库访问。
实现:
// Redis 缓存号段
Redis: SET order_segment:1000
// 本地使用
if (localCounter < cachedMax) {
return ++localCounter;
}
// 缓存用完,获取新号段性能测试
1. 测试方法
工具:
JMeter:压力测试
wrk:HTTP 压力测试
ab:Apache Benchmark
自定义脚本:模拟真实场景2. 测试指标
基准测试:
- 单线程性能
- 多线程性能
- 并发性能
- 持续运行性能压力测试:
- 极限 QPS
- 系统稳定性
- 故障恢复
- 资源使用3. 测试结果示例
UUID 生成:
QPS:1,000,000+
延迟:< 0.001ms
CPU:单核 80%数据库自增:
QPS:50,000
延迟:5ms
数据库:100% CPU雪花算法:
QPS:500,000
延迟:< 0.01ms
CPU:单核 30%性能优化最佳实践
1. 性能优先级
1. 避免网络调用 → 本地生成
2. 减少数据库访问 → 批量获取
3. 避免锁竞争 → 无锁设计
4. 使用缓存 → 减少计算2. 性能监控
监控指标:
- QPS
- 延迟(P50, P99, P999)
- 错误率
- 资源使用(CPU, 内存, 网络)
- 数据库连接数3. 性能调优
调优步骤:
1. 基准测试
2. 识别瓶颈
3. 针对性优化
4. 重新测试
5. 迭代改进常见错误
错误1:忽视性能影响
❌ 错误做法:
每次生成 ID 都调用远程服务
✅ 正确做法:
本地生成,减少网络调用错误2:过度优化
❌ 错误做法:
为了性能牺牲唯一性
✅ 正确做法:
在保证唯一性的前提下优化性能错误3:没有压力测试
❌ 错误做法:
只在开发环境测试
✅ 正确做法:
在生产环境模拟高并发性能检查清单
在实现分布式 ID 生成器时,确保:
- 明确性能指标要求
- 进行性能基准测试
- 识别性能瓶颈
- 优化网络调用
- 优化数据库访问
- 优化锁竞争
- 使用批量获取
- 使用双 Buffer
- 进行压力测试
- 监控性能指标
- 建立性能告警
下一步
了解了性能要求后,我们继续学习 ID 的高可用性要求。
👉 下一节:可用性