3

有一个令人费解的要求。

基本上我需要用这些标准创建唯一的 id

  • 9位数字,当天唯一(表示第二天再次出现该数字即可)
  • 实时生成;仅限 java (意味着没有从数据库生成序列号 - 实际上根本没有数据库访问权限)
  • 生成该数字以填充 requestID,每天将生成大约 1.000.000 个 id。
  • 不应使用 UUID 或 UID(超过 9 位)

这是我的考虑:

  • 使用序列号听起来不错,但万一 JVM 重启,requestId 可能会重新生成。
  • 使用时间 HHmmssSSS(时分秒毫秒)有 2 个问题:

一种。服务器管理员可能会更改系统时间。
湾。如果在同一毫秒内询问 2 个请求,可能会导致问题。

任何的想法?

4

4 回答 4

5

没有从数据库生成序列号

我讨厌这样愚蠢的要求。我说你作弊并使用像H2HSQLDB这样的嵌入式数据库,并通过序列生成标识符。

编辑:让我扩展一下为什么我提出这个“作弊”:我对“无数据库”要求的理解是不应该安装任何数据库软件来处理这个要求,或者现有的数据库模式不能改变。使用嵌入式数据库与将新的 jar 文件添加到项目中是一样的。为什么你不应该这样做?当关系数据库已经为您解决了这个问题时,为什么还要自己实现一些东西呢?

于 2012-07-24T08:09:56.300 回答
2

处理 1,000,000 个 ID 的九位数字为我们提供了三位数字(我们需要其他六位数字作为 0-999999 的 ID)。

我假设您有一个多服务器设置。为每个服务器分配一个三位数的服务器 ID,然后您可以在每个服务器内分配唯一的 ID 值,而不必担心它们之间的重叠。它可以只是内存中不断增加的值,除了在 JVM 重新启动后仍然存在,我们需要将最近分配的值回显到磁盘(嗯,到任何你想要存储它的地方——本地磁盘、内存缓存等等)。

为了确保您不会在每个请求上遇到文件/任何 I/O 的开销,您在块中分配 ID,将块的端点回显到存储。

所以它最终是:

  • 给每个服务器一个 ID
  • 在服务器上存储当天最后分配的值(例如文件)
  • 让 ID 分配器在块中工作(一次 10 个 ID,100 个,等等)
  • 分配块:
    • 读取文件,写回一个由你的块大小增加的数字
  • 使用块中的 ID
  • ID 将是,例如 12000000027 表示服务器 #12 分配的第 28 个 ID
  • 当一天改变时(例如,午夜),扔掉你当前的块并为新的一天分配一个新的块

伪代码中:

class IDAllocator {
    Storage storage;
    int     nextId;
    int     idCount;
    int     blockSize;
    long    lastIdTime;

    /**
     * Creates an IDAllocator with the given server ID, backing storage,
     * and block size.
     *
     * @param   serverId        the ID of the server (e.g., 12)
     * @param   s               the backing storage to use
     * @param   size            the block size to use
     * @throws  SomeException   if something goes wrong
     */
    IDAllocator(int serverId, Storage s, int size)
    throws SomeException {

        // Remember our info
        this.serverId = serverId * 1000000; // Add a million to make life easy
        this.storage = s;
        this.nextId = 0;
        this.idCount = 0;
        this.blockSize = bs;
        this.lastIdTime = this.getDayMilliseconds();

        // Get the first block. If you like and depending on
        // what container this code is running in, you could
        // spin this out to a separate thread.
        this.getBlock();
    }

    public synchronized int getNextId()
    throws SomeException {
        int id;

        // If we're out of IDs, or if the day has changed, get a new block
        if (idCount == 0 || this.lastIdTime < this.getDayMilliseconds()) {
            this.getBlock();
        }

        // Alloc from the block    
        id = this.nextId;
        --this.idCount;
        ++this.nextId;

        // If you wanted (and depending on what container this
        // code is running in), you could proactively retrieve
        // the next block here if you were getting low...

        // Return the ID
        return id + this.serverId;
    }

    protected long getDayMilliseconds() {
        return System.currentTimeMillis() % 86400000;
    }

    protected void getBlock()
    throws SomeException {
        int id;

        synchronized (this) {
            synchronized (this.storage.syncRoot()) {
                id = this.storage.readIntFromStorage();
                this.storage.writeIntToStroage(id + blocksize);
            }

            this.nextId = id;
            this.idCount = blocksize;
        }
    }
}

...但同样,这是伪代码,您可能希望在其中投入一些主动的东西,这样您就不会在需要 ID 时阻塞 I/O 等待 ID。

以上是假设您已经拥有某种应用程序范围的单例,并且该IDAllocator实例只是该单个实例中的数据成员。如果没有,您可以轻松地将上述设置为单例,方法是为其提供经典getInstance方法并让它从环境中读取其配置,而不是将其作为构造函数的参数接收。

于 2012-07-24T08:18:06.230 回答
1

服务器 1 从 1 计数到 999.999.999 怎么样?服务器 2 从 -999.999.999 计数到 -1 怎么样。

我猜由于负载平衡,平衡将是大约 50:50。因此,您为每个服务器获得了相同的 id 范围。此外,您将最后生成的 id 存储在文件系统上。由于性能问题,只需存储每 1000 个值(或 10000 个,这并不重要)。重新启动您的应用程序后,读取最后生成的值并添加 1000。我想这会起作用。

于 2012-07-24T10:25:35.970 回答
0

您可以尝试 Apache 的RandomStringUtils String random(int count, boolean letters, boolean numbers)或尝试使用Java TRNG 客户端库,该库又使用 RANDOM.ORG:

该库提供与 Java 安全 API 集成的 SecureRandom 服务,用于访问 random.org 和 random.irb.hr(通过大气噪​​声或光子发射产生随机性的真正随机数生成器)。

我认为,如果您获得其中之一并将其与时间戳相结合,您应该得到您所追求的。

于 2012-07-24T08:13:15.670 回答