基于snowflake id 的 N 位唯一数字id 生成算法总结
文章目录
- 背景
- 方法一: snowflake id 取余
- 方法二: 哈希映射
- 方法三: 时间戳和序号拼接
- 方法四: 结合时间戳,机器码以及序列号
背景
分布式场景下选择snowflake id 生成算法来生成id是常见的技术选型,然而默认情况下snowflake id 生成器生成的id长度为19位数字,有些场景下我们需要的是8位,9位等长度小于19的唯一数字id,这个时候就需要将原有的snowflake id 进行一定的处理才能得到满足要求的指定为数的唯一数字id.本文详细总结一下常用的处理方法以及其优缺点.
假设需要生成的唯一数字id长度为10位.
方法一: snowflake id 取余
public static String testGenId() {
long snowflakeId= IdUtil.getSnowflake(1,1 ).nextId();
// 10 位
long res = snowflakeId % 10000000000L;
return String.valueOf(res);
}
优点:简单粗暴
缺点:高并发场景下id容器碰撞
方法二: 哈希映射
本质上通过hash运算将长整数映射为短整数
@SneakyThrows
public static String testGenId() {
long snowflakeId= IdUtil.getSnowflake(1,1 ).nextId();
String snowflakeStr = String.valueOf(snowflakeId);
// md5 hash 算法
MessageDigest md5 = MessageDigest.getInstance("MD5");
byte[] digest = md5.digest(snowflakeStr.getBytes(StandardCharsets.UTF_8));
// 16进制整数转换为10进制整数字符串
String hexString = new BigInteger(1, digest).toString(10);
// 截取前10位作为结果
return hexString.substring(0, 10);
}
优点:1. 更加随机且均匀分布的映射,减少碰撞. 2. 保持了 Snowflake ID 的唯一性和分布特性
缺点:1. 计算相对复杂,需要额外的哈希步骤. 2. 依赖哈希算法的特性,可能存在极小的碰撞概率.
方法三: 时间戳和序号拼接
本质结合snowflake id 中的时间戳和序号, 缩小为10位数.
回顾 snowflake id 组成:
首位为0
41位时间戳
10位机器码,高5位数据中心,低5位是工作节点id.
12位序列号
public static String testGenId() {
long snowflakeId= IdUtil.getSnowflake(1,1 ).nextId();
// 右移22位移出 机器码和序列号 按位运算即可得到时间戳
long timestamp = (snowflakeId >> 22) & 0x1FFFFFFFFFFL;
// 按位运算即可得sequence
long sequence = snowflakeId & 0xFFFL;
// 时间戳和序列号拼接结果
long result = timestamp * 1000 + sequence;
// 返回10位整数,位数不足则补0
return String.format("%010d", result % 10000000000L);
}
优点: 保证id唯一性和时间连续性
缺点: 丢失了snowflake机器码信息,只使用了时间戳和序列号
方法四: 结合时间戳,机器码以及序列号
public static String testGenId() {
long snowflakeId= IdUtil.getSnowflake(1,1 ).nextId();
// 右移22位移出 机器码和序列号 按位运算即可得到时间戳
long timestamp = (snowflakeId >> 22) & 0x1FFFFFFFFFFL;
// 机器码
long machine = (snowflakeId >> 12) & 0xFFL;
// 按位运算即可得sequence
long sequence = snowflakeId & 0xFFFL;
// 时间戳和序列号拼接结果
long result = timestamp * 100000L + machine * 1000 + sequence;
// 随机数
Random rand = new Random();
result += rand.nextInt(1000);
// 返回10位整数,位数不足则补0
return String.format("%010d", result % 10000000000L);
}
优点: 减小碰撞概率
缺点: snowflake各个位数组合方法可能对结果有影响.