如何在java中生成增量标识符

时间:2012-10-16 09:06:45

标签: java

我要求我不断收到需要写入文件的消息。每次收到新消息时,都需要将其写入单独的文件中。我想要的是生成一个唯一的标识符用作文件名。我也想保留消息的顺序。我的意思是,作为文件名生成的标识符应该始终是增量的。

我使用 UUID.randomUUID()来生成文件名,但这种方法的问题是UUID只保证标识符的随机性,但不是增量式的。结果我丢失了文件的顺序(我希望首先生成的文件应首先出现在列表中)。

已知方法

  1. 可以使用System.currentTimeMillis()但我可以在同一时间戳收到多条消息。
  2. 2.另一种方法可以是实现静态long值,并在创建文件时递增它,并将long值用作文件名。但我不确定这种方法。此外,它似乎不是我的问题的正确解决方案。我认为可能有比这更好的解决方案。

    如果有人可以建议我更好地解决这个问题,我们将非常感激。

3 个答案:

答案 0 :(得分:4)

如果您希望您的id值在服务器重新启动之间均匀上升,那么您必须以系统时间为基础,或者使用一些精心构建的强大逻辑来保留最后使用的ID。请注意,实现稳健性并不难,但以高性能和可扩展的方式实现它是。

如果您还需要在冗余服务器群集中的多个节点上使用id唯一,那么您需要更复杂的逻辑,这肯定涉及所有框同步访问的持久性存储。当然,提高性能是更难的。

我能看到的最佳选择是拥有一个很长的ID,因此这些部分有空间:

  1. System.currentTimeMillis用于长期唯一性(跨重启);
  2. System.nanotime以获得更精细的粒度;
  3. 每个服务器节点的唯一ID(以特定于平台的方式确定)。
  4. 该方法仍然需要记住生成的最后一个值,并在重复的情况下重试。但是,它不必重试太多次,直到下一个nanoTime时钟滴答 - 它甚至可能忙 - 等待它。

    没有第3点的代码草图(单节点实现):

    private static long lastNanos;
    public static synchronized String uniqueId() {
      for (;/*ever*/;) {
        final long n = System.nanoTime();
        if (n == lastNanos) continue;
        lastNanos = n;
        return "" + System.currentTimeMillis() + n;
      }
    }
    

答案 1 :(得分:0)

好的,我的举手。我的最后一个答案是相当不稳定的,我删除了它。

保持网站的精神,我以为我会尝试不同的tac。

如果您说您将这些邮件保存在一个文件中,那么您可以尝试创建一个超出文件大小的唯一ID吗?

在将消息写入文件之前,它的id可能是文件的当前大小。

如果这些消息需要在多个文件中唯一,您可以添加文件名+大小作为ID。

我会将同步的烫手山芋留到另一天。但是你可以将所有这些包装在一个跟踪事物的同步对象中。

此外,我假设将来不会删除写入该文件的任何邮件。

附加说明: 您可以创建一个消息处理对象,在构造时(或通过create方法)打开文件。 此对象将获取文件的初始大小,这将用作唯一ID。 随着每条消息的添加(以同步方式),id将增加消息的大小。 这将解决性能问题。如果多个JVM / Node访问同一个文件,则无效。

骨骼想法:

public class MessageSink {
    private long id = 0;

    public MessageSink(String filename) {
       id = ... get file size ..
    }

    public synchronized addMessage(Message msg) {
       msg.setId(id);
       .. write to file + flush ..
       .. or add to stack of messages that need to be written to file
       .. at a later stage.
       id = id + msg.getSize();
    }

    public void flushMessages() {
       .. open file
       .. for each message in stack write ...
       .. flush and close file
    }
}

答案 2 :(得分:0)

我有相同的要求并找到了合适的解决方案。 Twitter Snowflake使用一种简单的算法来生成可排序的64位(长)ID 。 Snowflake是在Scala上编写的,但方法很简单,可以很容易地在Java代码中使用。

id由以下内容组成: timestamp - 41位(毫秒级精度w /自定义时代给我们69年); machine id - 10位(MAC地址可用作硬件ID); 序列号 - 12位 - 每台机器每4096个滚动一次(带保护以避免在相同的时间内翻转)

公式如下:((timestamp - customEpoch) << timestampShift) | (machineId << machineIdShift) | sequenceNumber;

每个组件的移位取决于它在ID中的位位置。

详细说明和源代码可以在github找到:

Twitter Snowflake

Basic Java implementation of the Snowflake algorithm