C#雪花算法生成64位唯一ID,结构为1位符号位+41位时间戳+10位机器ID+12位序列号;需用Interlocked保证线程安全,处理时钟回拨,并确保workerId全局唯一且在0~1023范围内。

在C#中实现雪花算法(Snowflake ID),核心是按位组装一个64位长整型(long),结构为:1位符号位(固定为0)+ 41位时间戳(毫秒级,起始时间自定义)+ 10位机器ID(支持最多1024个节点)+ 12位序列号(每毫秒内可生成4096个ID)。关键在于线程安全、时钟回拨处理和ID唯一性保障。
基础结构与位运算设计
64位ID按如下方式划分(从高位到低位):
- 0:1位,保留位,始终为0(保证long为正数)
- timestamp:41位,毫秒时间戳(建议使用自定义纪元,如2020-01-01T00:00:00.000Z,避免溢出)
- datacenterId:5位(可选)+ workerId:5位 → 合计10位,共1024种组合
- sequence:12位,本毫秒内递增序号(0~4095),超限时等待至下一毫秒
推荐将 datacenterId 和 workerId 合并为一个 workerId(0~1023),简化部署。位移偏移量通常设为:timestampLeftShift = 22,workerIdLeftShift = 12,sequenceMask = 0xFFF(即4095)。
线程安全的ID生成器实现
使用 System.Threading.Interlocked 保证 sequence 和 lastTimestamp 的原子操作,避免锁开销:
- 用
private long _sequence = 0和private long _lastTimestamp = -1作为实例字段 - 每次生成前调用
Interlocked.CompareExchange(ref _lastTimestamp, timestamp, old)判断是否跨毫秒 - 同毫秒内通过
Interlocked.Increment(ref _sequence) & sequenceMask获取新序号 - 若 sequence 溢出(等于0),主动等待至下一毫秒再重试
注意:不要用 lock 或 Monitor,高并发下会成为瓶颈;静态类 + 单例模式更合适,确保每个服务实例有唯一 workerId。
时钟回拨容错处理
服务器时间向后调整(NTP校准)可能导致重复ID或阻塞。常见策略有:
- 容忍小范围回拨(如 ≤ 5ms):缓存最近一次合法时间戳,允许短暂回退并复用 sequence
- 拒绝生成:发现回拨直接抛异常(适合强一致性场景)
- 等待恢复:循环检测系统时间直到 ≥ 上次时间戳(慎用,可能卡死)
生产环境建议采用第一种:记录 _lastValidTimestamp,当 current 时抛异常;当 current 但差值≤5ms时,继续用原 timestamp + 递增 sequence。
WorkerId 分配与配置建议
workerId 必须全局唯一,不能靠随机或进程PID(易冲突)。推荐方式:
- 启动时从配置文件或环境变量读取(如
WORKER_ID=12) - 结合 Consul/Etcd 实现自动注册与分配(避免人工干预)
- K8s 场景下可用 Pod IP 的哈希后取模(
ip.GetHashCode() % 1024),需加防止单点哈希碰撞的兜底逻辑
务必校验 workerId ∈ [0, 1023],越界应启动失败并报错,不静默截断。
基本上就这些。只要注意位运算无误、sequence 原子更新、时间判断严谨、workerId 可控,一个轻量健壮的 Snowflake ID 生成器就完成了。不需要第三方库,.NET Core 3.1+ 原生支持完全足够。









