如何生成最长7位、无需校验历史的唯一随机字母数字UID?
你当前的实现存在明确的唯一性风险:32位随机值仅有约42亿种可能,受生日悖论影响,生成量达到7.7万时碰撞概率就达到50%,生成量到100万时碰撞概率几乎为100%,完全无法满足硬性唯一要求。
方案1:基于自增ID混淆(100%无碰撞,优先推荐)
你当前表已经有自增主键,这是天然的全局唯一因子,对自增ID做可逆混淆后,即可得到无规律、随机感强、且绝对唯一的UID,完全不需要提前查询历史UID校验。
- 核心逻辑:
- 字符集选择Base36(0-9、A-Z共36个字符),7位Base36最多可覆盖
36^7 = 783亿个值,足够支撑绝大多数业务的规模需求,生成的UID格式和你给出的PNR样例完全一致。 - 用「固定盐异或+位置换」的方式混淆自增ID,彻底打乱ID的有序性,避免被遍历猜解:
- 提前生成一个36位的随机固定盐,上线后不可修改
- 将自增ID和盐做异或运算
- 对运算结果的二进制位做固定顺序的置换,进一步打乱原有规律
- 将混淆后的数值转为Base36大写即可,不足7位无需补位也可正常使用。
- 字符集选择Base36(0-9、A-Z共36个字符),7位Base36最多可覆盖
- 示例代码:
// 固定盐值,上线后禁止修改,可自定义为其他随机值 private static final long SALT = 0X2F7D9A4C1L; // 36位二进制的置换顺序表,可自定义调整顺序 private static final int[] PERMUTE_TABLE = {3,1,4,0,2,6,5,9,7,10,8,12,15,11,13,14,17,19,16,22,18,20,24,21,23,26,28,25,31,27,29,33,30,32,35,34}; public static String generateUid(long autoIncrementId) { // 第一步:异或混淆 long confusedId = autoIncrementId ^ SALT; // 第二步:位置换 long permutedId = 0; for (int i = 0; i < 36; i++) { permutedId |= ((confusedId >> i) & 1) << PERMUTE_TABLE[i]; } // 第三步:转Base36大写 return Long.toString(permutedId, 36).toUpperCase(); }
- 优势:生成效率极高,绝对不会出现碰撞,UID无连续规律无法被遍历,长度控制在6-7位符合要求。
方案2:低碰撞概率纯随机方案(适合生成UID时未拿到自增ID的场景)
如果生成UID时还未执行数据库插入操作,无法提前获取自增ID,可以通过提升随机位长度将碰撞概率降到可忽略的程度,同时配合数据库唯一索引兜底,不需要提前查库校验。
- 核心逻辑:
- 改用Base36编码代替你当前用的Base32,相同长度下信息量更高。
- 生成36位的SecureRandom真随机值,刚好可以用7位Base36完整承载。
- 给数据库UID字段添加唯一索引,极端情况下出现碰撞时,数据库会抛出唯一约束异常,上层仅需做1-2次重试即可,不需要提前查询数据库。
- 碰撞概率说明:36位随机值场景下,生成100万条UID的碰撞概率仅为0.0001%,生成1000万条的碰撞概率仅为1%,绝大多数业务场景下完全可以忽略风险。
- 改进后的代码:
BigInteger big = new BigInteger(36, new SecureRandom()); return big.toString(36).toUpperCase();
内容的提问来源于stack exchange,提问作者Muhammad Umer
相关产品推荐
相关产品推荐

