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 的示例代码:
java 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 的原子变量来保证并发安全。