直接用 time.now().unixnano() 不行,因高并发下纳秒级重复且多实例无隔离;雪花算法靠机器id+序列号防冲突;bwmarrin/snowflake 库需显式设唯一nodeid、处理时钟回拨、避免node复用;位数权衡需按扩容需求调整;id存储须用uint64和bigint unsigned,json传字符串防精度丢失。

为什么直接用 time.Now().UnixNano() 不行
时间戳本身不唯一,高并发下同一纳秒内多次调用会撞车;单机还行,一上多实例立刻重复。雪花算法核心不是“时间”,而是靠机器 ID + 序列号做冲突隔离。
github.com/bwmarrin/snowflake 的坑在哪
这个库默认用 nodeID 做机器标识,但没强制校验唯一性——如果多个服务没配不同 nodeID,ID 就直接重复。而且它初始化时会检查系统时钟是否回拨,回拨超过 5ms 就 panic,线上 NTP 同步或虚拟机休眠后很常见。
初始化并管理阿里云SDK Go 客户端,涵盖 sync.Once 单例、goroutine 安全、endpoint 与 region 配置、VPC 端点及调试。
- 必须显式传入唯一
nodeID:sf.NewNode(1),别依赖默认值 - 生产环境建议包装一层重试逻辑,捕获
snowflake.ErrInvalidSystemTime后 sleep 等时钟恢复 - 注意它的
Node实例不是 goroutine-safe 的,别跨协程共用一个Node
自己实现时,时间戳位数和序列号怎么权衡
标准雪花是 41bit 时间戳(约 69 年)、10bit 机器 ID(最多 1024 节点)、12bit 序列号(每毫秒最多 4096 个 ID)。Golang 的 int64 刚好够,但你要根据实际扩容需求调整:
- 如果确定永远不超过 64 台机器,可以把机器 ID 缩到 6bit,把序列号扩到 16bit(单毫秒 65536 个 ID)
- 时间戳起点别硬写
2020-01-01,用time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC).UnixMilli()避免本地时区偏差 - 序列号溢出时不能简单归零——要等下一毫秒,否则可能生成重复 ID
生成的 ID 为什么存数据库要用 BIGINT UNSIGNED
标准雪花 ID 是 64bit 无符号整数,最高位是 0,但 Golang 的 int64 是有符号的。如果你用 int64 接收再存 MySQL 的 BIGINT(有符号),遇到高位为 1 的 ID(比如时间戳很长以后)就会溢出变负数,查不出来也插不进。
- Go 侧推荐用
uint64存 ID,别转int64 - MySQL 字段必须是
BIGINT UNSIGNED,否则范围不够且比较逻辑错乱 - JSON 序列化时注意:
uint64超过2^53-1会被 JS 丢失精度,前端要用字符串传
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










