在分布式系统中,唯一标识符(UUID, Universally Unique Identifier)是一种非常重要的工具,它帮助开发者为每个对象、记录、事务等分配一个独一无二的标识。UUID 被广泛应用于数据库主键生成、会话标识、文件命名等场景。本文将对 UUID 的概念、格式、版本及在 Java 中的使用进行详细介绍。
1. UUID 概述
UUID,全称为 Universally Unique Identifier(通用唯一标识符),由一组 32 位十六进制数字组成,并按 8-4-4-4-12 的格式分为五段,每段由连字号 - 分隔。其标准格式如下所示:
xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx
其中:
- M 表示 UUID 的版本,目前 UUID 支持 1 到 5 共五个版本。
- N 表示 UUID 的变体,常见的变体为 8、9、a、b。
例如,UUID
30385d15-0a88-42eb-bc43-2c000e9f778c 中,42 表示这是一个版本 4 的 UUID,且变体为 b。
2. UUID 版本
UUID 支持五个版本,每个版本根据生成算法的不同,用于不同的场景和需求。
2.1 UUID Version 1 —— 基于时间的 UUID
这种 UUID 是通过计算当前时间戳、随机数和机器 MAC 地址生成的。由于包含了机器的 MAC 地址,它能保证在全球范围内的唯一性。然而,使用 MAC 地址会暴露机器信息,因此可能存在一定的安全隐患。一般情况下,这个版本的 UUID 多用于局域网内。
2.2 UUID Version 2 —— DCE 安全的 UUID
此版本与 Version 1 相似,但它将时间戳的前 4 位替换为 POSIX 的 UID 或 GID。这种 UUID 多用于分布式计算环境(DCE),并不常见。
2.3 UUID Version 3 —— 基于名字的 UUID(MD5)
通过计算名字和名字空间的 MD5 散列值生成 UUID。这种 UUID 保证了相同命名空间中相同名字的 UUID 是相同的,不同名字生成的 UUID 是唯一的。它适用于需要基于名字生成固定 UUID 的场景。
2.4 UUID Version 4 —— 随机 UUID
根据随机数(或伪随机数)生成 UUID。虽然版本 4 的 UUID 存在一定的重复概率,但生成过程中高度随机性使得冲突几乎不可能。它是最常用的 UUID 版本,尤其在分布式系统中。
2.5 UUID Version 5 —— 基于名字的 UUID(SHA-1)
与版本 3 类似,版本 5 使用 SHA-1 算法来计算名字和名字空间的散列值。这种方式生成的 UUID 在确保唯一性的同时,也提供了更高的安全性,适用于需要基于名字生成且要求更高安全性的数据标识。
3. 在 Java 中生成 UUID
在 Java 中,UUID 是通过 java.util.UUID 类提供的。常见的生成方式有随机生成(版本 4)和基于名字生成(版本 3 或 5)。
3.1 生成随机 UUID(版本 4)
UUID randomUUID = UUID.randomUUID();
System.out.println(randomUUID);
这将生成一个版本为 4 的 UUID,适用于大多数场景。
3.2 生成基于名字的 UUID(版本 5)
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.UUID;
public class UUID5 {
public static UUID fromUTF8(String name) {
return fromBytes(name.getBytes(StandardCharsets.UTF_8));
}
private static UUID fromBytes(byte[] name) {
if (name == null) {
throw new NullPointerException("name == null");
}
try {
MessageDigest md = MessageDigest.getInstance("SHA-1");
return makeUUID(md.digest(name), 5);
} catch (NoSuchAlgorithmException e) {
throw new AssertionError(e);
}
}
private static UUID makeUUID(byte[] hash, int version) {
long msb = peekLong(hash, 0, ByteOrder.BIG_ENDIAN);
long lsb = peekLong(hash, 8, ByteOrder.BIG_ENDIAN);
msb &= ~(0xfL << 12);
msb |= ((long) version) << 12;
lsb &= ~(0x3L << 62);
lsb |= 2L << 62;
return new UUID(msb, lsb);
}
private static long peekLong(final byte[] src, final int offset, final ByteOrder order) {
long ans = 0;
if (order == ByteOrder.BIG_ENDIAN) {
for (int i = offset; i < offset + 8; i++) {
ans <<= 8;
ans |= src[i] & 0xffL;
}
} else {
for (int i = offset + 7; i >= offset; i--) {
ans <<= 8;
ans |= src[i] & 0xffL;
}
}
return ans;
}
public static void main(String[] args) {
String name = "example@example.com";
UUID uuid = fromUTF8(name);
System.out.println("Generated UUID: " + uuid);
}
}4. 使用第三方库生成 UUID
Java 标准库已提供了 UUID 生成的基本功能,但如果你需要更多功能,例如更高效的 UUID 生成,或者更方便的生成方式,可以使用第三方库 java-uuid-generator。该库提供了更丰富的 UUID 生成选项,并且支持更高级的特性。
在 Maven 中集成该库:
<dependency>
<groupId>com.fasterxml.uuid</groupId>
<artifactId>java-uuid-generator</artifactId>
<version>3.1.5</version>
</dependency>
使用 UUIDGenerators 类可以轻松生成时间戳 UUID(Version 1)和随机 UUID(Version 4)等:
import com.fasterxml.uuid.Generators;
public class UUIDTest {
public static void main(String[] args) {
// 生成基于时间的 UUID(Version 1)
UUID timeBasedUUID = Generators.timeBasedGenerator().generate();
System.out.println("Time-based UUID: " + timeBasedUUID);
// 生成随机 UUID(Version 4)
UUID randomUUID = UUID.randomUUID();
System.out.println("Random UUID: " + randomUUID);
}
}5. 总结
UUID 是一种用于唯一标识符生成的工具,它能保证在分布式环境中对象的唯一性。无论是基于时间的 UUID,还是基于名字的 UUID,每种类型都有其适用的场景。在 Java 中,生成 UUID 非常方便,既可以使用标准库,也可以利用第三方库实现更复杂的 UUID 生成。希望本文能帮助你更好地理解 UUID,并有效应用到实际项目中。
