什么是ULID
ULID(Universally Unique Lexicographically Sortable Identifier)是一种生成全局唯一且可按字典序排序的标识符的算法。它是在UUID(Universally Unique Identifier)的基础上进行改进的,旨在解决UUID不易排序的问题。
ULID的结构与特点
ULID由128位组成,可以分为两个部分:时间戳部分和随机数部分。
时间戳部分由48位组成,以微秒为单位表示生成ULID的时间。ULID使用的是Unix时间戳,即从1970年1月1日开始计算的毫秒数。
随机数部分由80位组成,用于保证生成的ULID具有足够的随机性。
ULID的格式如下:
01AN4Z07BY 79KA1307SR9X4MV3
|----------| |----------------|
Timestamp Randomness
10 chars 16 chars
ULID的生成算法
ULID的生成算法相对较为简单,可以分为以下几个步骤:
- 获取当前时间戳(单位为微秒),并将其转化为48位的二进制数。
- 将时间戳的二进制数进行左移,以腾出足够的空间存放随机数。
- 生成随机数部分,具体的生成方法可以使用伪随机数生成器(PRNG)或者真随机数生成器(TRNG)。
- 将时间戳部分和随机数部分进行合并。
- 将合并后的128位二进制数转化为32位的十六进制字符串。
- 根据需要,可以将ULID进行进一步的编码,例如Base32编码。
下面是用Java实现ULID生成算法的示例代码:
在上面的示例代码中,我们使用了java.security.SecureRandom
类来生成随机数。同时,我们使用了Java 8中的java.time.Instant
类来获取当前的时间戳。ULID的生成逻辑非常简单,只需要将时间戳部分左移,然后与随机数部分进行合并即可。
以下是使用示例代码生成ULID的示例:
ULID的应用场景
ULID主要应用于需要生成全局唯一且可按字典序排序的标识符的场景。由于ULID具有以下特点,因此在一些特定的应用场景中非常适用:
- 全局唯一性:ULID的生成算法保证了生成的标识符在全局范围内是唯一的。
- 字典序排序:由于ULID使用的是时间戳作为排序依据,因此生成的ULID可以按照生成的顺序进行排序。
- 可读性:ULID生成的标识符是由字母和数字组成的,可以直接用于显示和传输。
ULID可以应用于多个领域,例如数据库索引、分布式系统、日志记录等。
ULID的优点
ULID(Universally Unique Lexicographically Sortable Identifier)是一种能够生成全局唯一的识别码的方法,具有以下优点:
-
全局唯一性:ULID生成的识别码具有全局唯一性,不同机器、不同时间、不同线程、不同进程生成的ULID都不会重复。
-
高容量:ULID由128位二进制数表示,相对于传统的UUID(只有128位)能够容纳更多的信息。
-
可排序:ULID具有词典序(字典序)排序特性,当两个ULID做比较时,按照字符顺序比较即可得到正确的排序结果。
-
高性能:ULID的生成速度比传统的UUID更快,使用标准的开源实现一次生成的时间不到毫秒级别。
-
高可读性:ULID采用了很多不易混淆的字符,具有一定的可读性,便于在日志、调试信息中使用。
ULID是一种高效、全局唯一、可排序、容量大、可读性强的识别码生成算法,适合于分布式环境下的唯一标识生成。
UUID (Universally Unique Identifier) 和 ULID (Universally Unique Lexicographically Sortable Identifier) 都是用来生成唯一标识符的算法,但它们在实现方式上有所不同。
UUID 是一种由标准化组织制定的标识符生成算法,它通过 MAC 地址、时间戳、命名空间和随机数等数据生成,保证了生成的标识符的唯一性。它的长度为 128 位,通常表示为 32 个十六进制数字,其中包含 4 个破折号,如:“550e8400-e29b-41d4-a716-446655440000”。
ULID 是基于时间和随机数生成的标识符算法,它的结构是包含了时间戳的 48 位前缀和 80 位随机数后缀,共计 128 位。ULID 也可以表示为 32 个字符的字符串,而且这个字符串是根据时间戳顺序排列的,因此可以用作排序和索引。
总的来说,UUID 和 ULID 都是用来生成唯一标识符的算法,但它们的实现方式、长度、格式等方面存在差异。选择使用哪种算法取决于场景需求,如果需要标识符完全随机且数量大到足以保证唯一性,使用 UUID 就可以了;如果需要标识符的排序和索引,并且时间戳是必须的,那么可以使用 ULID。
ULID (Universally Unique Lexicographically Sortable Identifier) 是一个可排序且唯一的标识符,它由时间戳和随机数生成。相较于 UUID,ULID 可以更好地支持分布式系统的排序和查询。以下是 Java 实现 ULID 的示例代码:
import java.security.SecureRandom;
import java.time.Instant;
import java.util.concurrent.atomic.AtomicLong;
public class ULIDGenerator {
private static final long EPOCH = Instant.parse("2021-01-01T00:00:00Z").toEpochMilli();
private static final char[] ENCODING = "0123456789ABCDEFGHJKMNPQRSTVWXYZ".toCharArray();
private static final int ENCODING_LENGTH = ENCODING.length;
private static final int TIMESTAMP_LENGTH = 10;
private static final int RANDOMNESS_LENGTH = 16;
private static final AtomicLong LAST_TIMESTAMP_MS = new AtomicLong(Long.MIN_VALUE);
private static final SecureRandom RANDOM = new SecureRandom();
public static String generateULID() {
long timestampMs = getCurrentTimestampMs();
StringBuilder sb = new StringBuilder(TIMESTAMP_LENGTH + RANDOMNESS_LENGTH);
encodeLong(timestampMs - EPOCH, sb, TIMESTAMP_LENGTH); // 时间戳
encodeLong(generateRandomness(), sb, RANDOMNESS_LENGTH); // 随机数
return sb.toString();
}
private static long getCurrentTimestampMs() {
long now = Instant.now().toEpochMilli();
while (true) {
long lastTimestampMs = LAST_TIMESTAMP_MS.get();
if (now > lastTimestampMs) {
if (LAST_TIMESTAMP_MS.compareAndSet(lastTimestampMs, now)) {
return now;
}
} else {
return LAST_TIMESTAMP_MS.incrementAndGet();
}
}
}
private static long generateRandomness() {
byte[] randomness = new byte[8];
RANDOM.nextBytes(randomness);
return bytesToLong(randomness);
}
private static void encodeLong(long value, StringBuilder sb, int length) {
sb.setLength(length);
for (int i = length - 1; i >= 0; i--) {
sb.setCharAt(i, ENCODING[(int) (value % ENCODING_LENGTH)]);
value /= ENCODING_LENGTH;
}
}
private static long bytesToLong(byte[] bytes) {
long value = 0;
for (byte b : bytes) {
value = (value << 8) | (b & 0xff);
}
return value;
}
}
上述代码使用了 SecureRandom 生成 128 位的随机数,然后使用自定义的一种 32 个字符的编码(0123456789ABCDEFGHJKMNPQRSTVWXYZ)
对随机数和时间戳进行编码。时间戳表示自“2021-01-01T00:00:00Z”
以来经过的毫秒数,长度为 10 个字符。到达极限时,ULID 可以支持生成超过 3.4e+38 个 ID,每秒达到 280 万个 ID 的生成速度。
需要注意的是,由于 ULID 是有序的,所以在高并发的场景下,使用静态的 LAST_TIMESTAMP_MS 可能会成为瓶颈,可以考虑使用类似 AtomicLong 的原子变量来保证并发安全。
总结
ULID是一种生成全局唯一且可按字典序排序的标识符的算法,它解决了UUID不易排序的问题。ULID的生成算法非常简单,主要由时间戳部分和随机数部分组成。ULID具有全局唯一性、字典序排序和可读性的特点,适用于多个领域的应用场景。