一种比 UUID 更短的 ID 生成方式推荐

在分布式系统中,常常需要生成唯一的 traceID 用来跟踪一次用户请求的调用链。

系统之前一直使用的是 UUID,由于 UUID 是 32 位,比较长,而且不携带任何业务相关信息。除了唯一好像基本没其它优点。

于是想着有没有一种更好的 ID 生成方式?

思路:

  1. 使用 62 进制格式,可充分利用数字和字母大小写组合,压缩效率高,又避免 64 进制出现的两个特殊字符在传输过程中可能出现的问题。
  2. 使用随机数 + 时间戳的组合方式,固定生成 10 位。
    1. 前七位使用精确到毫秒的时间戳,转成 62 进制方式。7 位的 62 进制可以使用到 2080 年,基本足够用了。
    2. 后三位使用随机数填充,碰撞概率为:1/238327。

优点:

  1. 生成的串比较短,只有 10 位。
  2. 理论上,在一毫秒内重复的概率为:1/238327,已经非常小了。

附 Java 版实现源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
import java.security.SecureRandom;

import org.apache.commons.lang.StringUtils;

public class IdUtils {

/**
* ID 生成器(三位随机数 + 时间戳)
*/
public static final String generateId() {
try {
int i = SecureRandom.getInstance("SHA1PRNG").nextInt(62 * 62 * 62 - 1);
String time = StringUtils.leftPad(To62String(System.currentTimeMillis()), 7, '0'); // 前 7 位是时间戳,精确到毫秒。
String random = StringUtils.leftPad(To62String(i), 3, '0'); // 后三位是随机数
return time + random;
} catch (Exception e) {
throw new RuntimeException("ID 生成失败");
}
}

/** Base64 字符 */
private static final char[] DIGITS = {
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'a', 'b', 'c', 'd', 'e', 'f',
'g', 'h', 'i', 'j', 'k', 'l',
'm', 'n', 'o', 'p', 'q', 'r',
's', 't', 'u', 'v', 'w', 'x',
'y', 'z',
'A', 'B', 'C', 'D', 'E', 'F',
'G', 'H', 'I', 'J', 'K', 'L',
'M', 'N', 'O', 'P', 'Q', 'R',
'S', 'T', 'U', 'V', 'W', 'X',
'Y', 'Z',
'*', '-'
};

/**
* 10 进制转成 62 进制
*/
private static final String To62String(long timeMillis) {
int mask = 62;
int bufLength = 11;
int charPos = bufLength;
char[] buf = new char[bufLength];
do {
buf[--charPos] = DIGITS[(int) (timeMillis % mask)];
timeMillis = timeMillis / mask;
} while (timeMillis > 0);
return new String(buf, charPos, (bufLength - charPos));
}
}