golang如何设计分布式ID生成系统_golang分布式ID生成系统设计指南
分布式ID不能直接用time.Now().UnixNano()因高并发下纳秒级重复、时钟回拨、容器漂移、随机数种子相同等导致ID冲突与乱序需用Sonyflake或自研Snowflake并妥善处理workerId分配与时钟治理。为什么不能直接用 time.Now().UnixNano() 拼接做分布式ID时间戳本身不唯一高并发下同一纳秒内多个节点或协程会生成重复ID单机多进程、容器重启、系统时钟回拨都会让顺序性崩坏。哪怕加随机数或机器号也解决不了时钟问题带来的 ID 冲突和乱序。回拨 1ms 就可能触发大量重复 workerId sequence 组合容器漂移后 hostname 或 MAC 地址可能复用导致 workerId 冲突math/rand 默认种子相同多实例并行时初始 sequence 易撞车用 github.com/sony/sonyflake 快速落地但要注意三点它比雪花算法Snowflake更轻用毫秒时间戳 机器 ID 序列号且内置了基于 /dev/urandom 的自增 fallback 机制适合中小规模场景。但它默认不持久化 last time重启后若时钟未前进会拒绝发号。必须传入自定义 StartTime建议设为服务上线时间如 time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC)避免首次启动时因系统时间早于默认值而 panicMachineID 函数不能返回固定值推荐用 hostname pid 哈希或从环境变量读 WORKER_ID否则 K8s 里 Pod 重建就撞 ID它不处理时钟回拨需配合 sync.Once 状态检查在 NextID() 失败时主动 sleep 等待时钟追上自己实现 Snowflake 时 sequence 怎么不丢不重核心是用原子操作保序而不是锁或 channel——后者在每秒百万级调用下会成为瓶颈。Go 的 atomic.Uint64 足够支撑单机每毫秒上万 ID。每次获取 ID 前先 atomic.AddUint64(s.seq, 1)再与阈值如 4095取模溢出则阻塞等待下一毫秒不要把 seq 存 struct 字段里得用指针传参或闭包捕获否则 goroutine 间看到的是副本测试时用 runtime.Gosched() 模拟调度切换验证多 goroutine 并发调用是否仍严格递增要不要存数据库做 ID 号段看吞吐和一致性要求号段模式如 Leaf-segment本质是批量预占降低 DB 压力但引入双写风险和号段浪费。它适合 ID 需要全局强单调、且能容忍少量跳号的业务比如订单号展示。 Cleanup.pictures 智能移除图片中的物体、文本、污迹、人物或任何不想要的东西