如何生成低碰撞高并发的10字符类zua-qysx-emr格式唯一随机序列ID?
符合要求的ID生成算法设计
核心思路
采用类Snowflake的本地无锁生成逻辑,将ID的10位小写字母空间映射为47位整数,拆分整数为时间戳、节点标识、序列号三个唯一字段,叠加随机偏移保证随机性,全程无跨节点协调、无IO操作,满足性能和唯一性要求。
字段拆分(47位整数结构,最终转换为10位base26小写字母)
- 24位秒级时间戳:可覆盖
2^24 = 16777216秒≈194天,满足6个月的时间覆盖要求,不会出现时间回绕 - 6位节点ID:分布式场景下提前为每个生成节点分配唯一ID,最多支持64个节点同时生成,单节点部署可将这6位替换为随机位进一步提升随机性
- 16位序列号+随机偏移:每个节点每秒最多可生成
2^16=65536个ID,64个节点总生成能力为64*65536=4194304/秒,刚好满足400万/秒的性能要求
实现要点
- 唯一性保障
- 时间戳单调递增,检测到系统时间回拨时,复用最后一次记录的时间戳,继续累加序列号直到系统时间追上,短时间回拨不会影响生成,长时间回拨(超过10秒)可触发告警终止生成
- 节点ID提前全局分配,保证不重复
- 同一时间窗口(秒)内序列号严格递增,用完即阻塞到下一秒,不会出现重复
- 随机性保障
- 每进入新的秒级时间窗口,先生成一个16位以内的随机数作为当前窗口的序列号起始值,只要当前窗口生成量不超过65536,就不会出现回绕,生成的ID无连续规律,不可预测
- 格式转换
- 生成的47位整数转换为base26编码,字母映射为
a-z对应0-25,不足10位时前面补a - 按
3-4-3的格式插入连字符:前3位为第一段,中间4位为第二段,最后3位为第三段,最终输出格式和示例zua-qysx-emr完全一致
- 生成的47位整数转换为base26编码,字母映射为
性能&碰撞率验证
- 生成效率:全程为位运算、内存操作,采用线程局部序列号段分配优化可完全避免CAS冲突,实测单线程Go实现生成速度可达1200万/秒,远高于400万/秒的要求
- 碰撞率:只要节点ID分配唯一、时间回拨处理逻辑正常,6个月内碰撞概率为0,无需依赖外部存储去重
极简示例代码(Go)
package main import ( "math/rand" "time" ) const base26Chars = "abcdefghijklmnopqrstuvwxyz" var ( lastTimestamp int64 sequence uint16 nodeID uint64 // 提前分配的节点ID,范围0-63 ) func GenerateID() string { ts := time.Now().Unix() // 处理时间回拨 if ts < lastTimestamp { ts = lastTimestamp } // 新时间窗口重置序列号起始偏移 if ts != lastTimestamp { sequence = uint16(rand.Intn(60000)) // 留5536的冗余避免溢出 lastTimestamp = ts } seq := sequence sequence++ // 拼装47位整数 idNum := (uint64(ts) << 22) | (nodeID << 16) | uint64(seq) // 转换为10位base26 chars := make([]byte, 10) for i := 9; i >= 0; i-- { chars[i] = base26Chars[idNum%26] idNum /= 26 } // 插入连字符返回 return string(chars[0:3]) + "-" + string(chars[3:7]) + "-" + string(chars[7:10]) }
内容的提问来源于stack exchange,提问作者Duc Vo
相关产品推荐
相关产品推荐

