性能要求

高性能是分布式 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 QPS

2. 延迟(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/ms

4. 并发度(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.001ms

2. 批量获取

原理:一次获取一批 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 次请求才访问数据库一次
性能提升:1000x

3. 双 Buffer 机制

原理:使用两个 Buffer,一个当前使用,一个预加载。

实现

Buffer1:[1000, 2000] ← 当前使用
Buffer2:[2000, 3000] ← 预加载

当 Buffer1 用完:
- 切换到 Buffer2
- 异步预加载 Buffer3

优势:
- 始终有可用 ID
- 零等待

4. 无锁设计

原理:避免使用锁,使用原子操作。

实现

性能对比

有锁:10,000 QPS
无锁:100,000 QPS
性能提升:10x

5. 预分配号段

原理:预先为每个节点分配独立的 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 的高可用性要求。

👉 下一节:可用性