毫秒并发 ID (MCID) 生成方案

1. 引言

在数字时代,唯一标识符的生成对于维护数据完整性、支持分布式系统以及确保准确的事务处理至关重要。毫秒并发 ID (MCID) 生成方案是一种高精度、可扩展的解决方案,旨在为需要毫秒级精度和高并发支持的实时系统生成唯一标识符。本文件概述了 MCID 方案的设计、实现及标准化建议,并特别强调了其对前端浏览器的支持能力。

2. 问题描述

随着现代应用程序和分布式系统的扩展,独特且一致的标识符生成方案变得日益重要。时间戳精度、高并发处理和确保分布式环境中的唯一性等挑战,迫切需要一种强大的 ID 生成策略。

传统的雪花 ID(Snowflake ID)生成方案使用 64 位二进制整数来生成唯一 ID。然而,由于前端浏览器的Number类型基于 IEEE 754 标准,只能精确表示到 53 位二进制位的整数。这导致了雪花 ID 通常需要转换为字符串格式才能在前端安全使用,增加了数据传输和处理的开销。MCID 方案的设计目的就是为了解决这个问题,提供一种不需要转换为字符串即可在前端安全使用的 ID 生成机制。

3. 目标

MCID 方案的主要目标包括:

  • 精度:利用毫秒级时间戳确保事件的及时排序。
  • 并发性:支持在同一毫秒内的高并发 ID 生成。
  • 可扩展性:提供一种在分布式系统中可扩展的机制,同时保持 ID 的唯一性。
  • 效率:生成紧凑的数字 ID,便于存储、索引和处理。
  • 前端兼容性:确保生成的 ID 能够在前端浏览器中安全地作为 Number 类型使用,避免精度损失。

4. 设计概述

MCID 方案通过结合三个组成部分生成唯一标识符:

  1. 时间戳部分:主要部分,表示自 Unix 纪元(1970 年 1 月 1 日 00:00:00 UTC)以来的当前毫秒数。
  2. 机器或进程标识符:次要部分,用于区分不同机器或进程生成的 ID,确保分布式环境中的唯一性。
  3. 序列号:在同一毫秒内生成多个 ID 时递增的计数器,处理高并发场景。

4.1 MCID 的结构

MCID 是一个不超过 53 位二进制的整数,由以下部分组成:

  • 时间戳(41 位):表示自 Unix 纪元以来的毫秒数。
  • 机器/进程 ID(8 位):用于区分生成 ID 的机器或进程,确保分布式环境中的唯一性。
  • 序列号(4 位):在同一毫秒内递增的计数器,支持每毫秒最多 16 个唯一 ID。

4.2 位分配

  • 41 位 用于时间戳:覆盖大约 69 年,这对于大多数应用场景来说是足够的。
  • 8 位 用于机器/进程 ID:支持多达 256 台机器或进程的分布式环境。
  • 4 位 用于序列号:支持每毫秒最多 16 个唯一 ID。

4.3 前端浏览器支持

MCID 生成的 ID 严格控制在 53 位二进制以内,这使得它可以安全地在前端浏览器中作为 Number 类型使用,而无需转换为字符串。这种设计有效避免了雪花 ID 在前端处理时需要转换为字符串的额外开销,从而提高了效率并简化了数据传输。

4.4 MCID 生成算法

  1. 时间戳部分:使用高精度系统时钟获取当前毫秒时间戳。
  2. 机器/进程 ID:使用预定义的机器或进程的唯一标识符。
  3. 序列号:如果在同一毫秒内生成多个 ID,则递增序列号。如果序列号超出最大值(15),则等待下一毫秒。

4.5 示例实现


5. 性能考虑

  • 时间精度:41 位的时间戳提供了毫秒级的精度,支持事件排序,对于时间敏感的应用至关重要。
  • 并发性:4 位的序列号确保每毫秒在单台机器或进程上可以生成多达 16 个唯一 ID,相比之前的方案大幅提升了并发处理能力,适合高并发环境。
  • 可扩展性:8 位的机器/进程 ID 支持多达 256 个分布式节点,每个节点能够独立生成 ID 而不会发生冲突。
  • 前端兼容性:生成的 ID 在前端浏览器中可以安全地作为 Number 类型使用,确保跨平台操作的一致性。

6. 适用场景

MCID 方案适用于广泛的应用场景,包括但不限于:

  • 分布式系统:需要在多个节点或进程之间生成唯一标识符。
  • 事务处理:确保金融系统中的唯一事务 ID。
  • 事件日志记录:以毫秒精度捕获和排序事件。
  • 数据同步:在需要跨分布式数据库生成全球唯一且有序的标识符的系统中。
  • Web 应用程序:需要在前端浏览器中处理唯一 ID,并确保其在 Number 类型下的精度和一致性。

7. 结论

毫秒并发 ID (MCID) 生成方案是一种强大、可扩展且高效的解决方案,能够在现代分布式系统中生成唯一标识符。通过利用毫秒级精度、支持高并发、确保全球唯一性,MCID 满足了当今高性能、实时应用的需求。其在前端浏览器中的良好支持确保了跨平台的一致性和安全性。同时,MCID 方案的设计避免了雪花 ID 在前端必须转换为字符串的缺陷,使其成为更加高效的替代方案。采用该方案将标准化唯一 ID 的生成,提高技术生态系统中的互操作性和可靠性。

8. Java 实现

package com.litongjava.open.chat.mcid;

public class McIdGenerator {
  private final int machineId;
  private int sequence = 0;
  private long lastTimestamp = -1L;

  public McIdGenerator(int machineId) {
    if (machineId < 0 || machineId > 255) { // 8位机器/进程ID限制
      throw new IllegalArgumentException("Machine ID must be between 0 and 255");
    }
    this.machineId = machineId;
  }

  public synchronized long generateId() {
    long timestamp = System.currentTimeMillis();

    if (timestamp == lastTimestamp) {
      sequence = (sequence + 1) & 15; // 4位序列号
      if (sequence == 0) {
        while (System.currentTimeMillis() <= timestamp) {
          // 等待下一毫秒
        }
        timestamp = System.currentTimeMillis();
      }
    } else {
      sequence = 0;
    }

    lastTimestamp = timestamp;
    // 41位时间戳 | 8位机器/进程ID | 4位序列号
    return ((timestamp & 0x1FFFFFFFFFFL) << 12) | (machineId << 4) | sequence;
  }

  public static void main(String[] args) {
    McIdGenerator generator = new McIdGenerator(1); // 机器ID = 1
    for(int i=0;i<10000;i++) {
      long id = generator.generateId();
      System.out.println(id); // 输出生成的唯一ID
    }

  }
}

tio-utils

import com.litongjava.tio.utils.mcid.McIdUtils;
long id = McIdUtils.id();