Java 实现 MongoDB ObjectId 算法
以下是 Java 实现 MongoDB ObjectId 算法的完整代码,该实现严格遵循其 12字节 结构规范,包含时间戳、机器标识、进程ID和计数器四部分。
import java.lang.management.ManagementFactory;
import java.lang.management.RuntimeMXBean;
import java.net.NetworkInterface;
import java.security.SecureRandom;
import java.time.Instant;
import java.util.Enumeration;
import java.util.concurrent.atomic.AtomicInteger;public class MongoObjectId {private static final int COUNTER_MAX = 0xFFFFFF; // 3字节最大值private static final AtomicInteger counter = new AtomicInteger(new SecureRandom().nextInt());private static final byte[] machineId = createMachineId();private static final byte[] processId = createProcessId();// 生成12字节ObjectIdpublic static byte[] generate() {byte[] objectId = new byte[12];// 4字节时间戳(秒级)int timestamp = (int) Instant.now().getEpochSecond();objectId[0] = (byte) (timestamp >> 24);objectId[1] = (byte) (timestamp >> 16);objectId[2] = (byte) (timestamp >> 8);objectId[3] = (byte) timestamp;// 3字节机器标识System.arraycopy(machineId, 0, objectId, 4, 3);// 2字节进程IDSystem.arraycopy(processId, 0, objectId, 7, 2);// 3字节计数器(线程安全)int cnt = counter.incrementAndGet() & COUNTER_MAX;objectId[9] = (byte) (cnt >> 16);objectId[10] = (byte) (cnt >> 8);objectId[11] = (byte) cnt;return objectId;}// 生成16进制字符串表示public static String toHexString(byte[] bytes) {StringBuilder sb = new StringBuilder();for (byte b : bytes) {sb.append(String.format("%02x", b));}return sb.toString();}// 获取3字节机器标识(MAC地址哈希)private static byte[] createMachineId() {try {Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();while (interfaces.hasMoreElements()) {NetworkInterface ni = interfaces.nextElement();if (!ni.isLoopback() && ni.getHardwareAddress() != null) {byte[] mac = ni.getHardwareAddress();return new byte[]{mac[0], mac[1], mac[2]};}}} catch (Exception e) {// 失败时使用随机数}return new byte[]{(byte) new SecureRandom().nextInt(),(byte) new SecureRandom().nextInt(),(byte) new SecureRandom().nextInt()};}// 获取2字节进程IDprivate static byte[] createProcessId() {RuntimeMXBean runtimeMxBean = ManagementFactory.getRuntimeMXBean();String processName = runtimeMxBean.getName();System.out.println("当前进程: " + processName);int pid = Integer.parseInt(processName.split("@")[0]);System.out.println("当前进程ID: " + pid);return new byte[]{(byte) (pid >> 8), (byte) pid};}public static void main(String[] args) {byte[] objectId = generate();System.out.println("生成的ObjectId: " + toHexString(objectId));objectId = generate();System.out.println("生成的ObjectId: " + toHexString(objectId));objectId = generate();System.out.println("生成的ObjectId: " + toHexString(objectId));}
}
实现特点:
1. 时间戳部分:采用Unix秒级时间戳,确保时间有序性
2. 机器标识:通过MAC地址哈希生成,保证分布式环境唯一性
3. 进程ID:通过ManagementFactory
和RuntimeMXBean
实现,这是最可靠且跨平台的方式
4. 原子计数器:确保单进程高并发下的唯一性
5. 最终输出符合MongoDB标准的24位十六进制格式
该实现严格遵循MongoDB ObjectId的规范:
- 总长度:12字节
- 组成结构:4字节时间戳 + 3字节机器标识 + 2字节进程ID + 3字节计数器
- 唯一性保证:同一进程、同一秒内最多可生成16777216(2^24)个不重复ID
实际应用场景:
- 分布式系统主键生成
- 日志追踪标识
- 需要时间排序的数据记录
- 替代UUIDv4的更高性能方案