【问题标题】:Generating a globally unique identifier in Java在 Java 中生成全局唯一标识符
【发布时间】:2010-09-16 14:45:00
【问题描述】:

总结:我正在开发一个持久性 Java Web 应用程序,我需要确保我持久化的所有资源都具有全局唯一标识符以防止重复。

细则:

  1. 我没有使用 RDBMS,所以我没有任何花哨的序列生成器(比如 Oracle 提供的那个)
  2. 我希望它更快,最好全部在内存中 - 我宁愿不必打开文件并增加一些值
  3. 它需要是线程安全的(我预计一次只有一个 JVM 需要生成 ID)
  4. JVM 实例之间需要保持一致性。如果服务器关闭并启动,ID 生成器不应重新生成它在先前实例化中生成的相同 ID(或者至少机会必须非常非常渺茫 - 我预计会有数百万的预置资源)李>
  5. 我已经看过 EJB 唯一 ID 模式文章中的示例。它们对我不起作用(我宁愿不完全依赖 System.currentTimeMillis(),因为我们将每毫秒保留多个资源)。
  6. 我查看了this question 中提出的答案。我对他们的担忧是,随着时间的推移,我获得重复 ID 的可能性有多大?我对使用java.util.UUID 代替UUID 的建议很感兴趣,但同样,重复的可能性需要非常小。
  7. 我正在使用 JDK6

【问题讨论】:

  • 您是否在不同的机器上运行应用程序的多个实例?如果是,您是否可能分批启动机器——以便多个进程可能在同一毫秒内启动?如果攻击者找到了导致 UUID 冲突的方法,这会危及您的应用程序的安全性吗?
  • (A) 会生成多少ID?多快? (每秒/分钟多少)(B)是的,UUID 正是为您的目的而发明的。

标签: java persistence uuid uniqueidentifier


【解决方案1】:

如果您想使用 java UUID 的更短更快的实现,请查看:

https://code.google.com/p/spf4j/source/browse/trunk/spf4j-core/src/main/java/org/spf4j/concurrent/UIDGenerator.java

查看 javadoc 中的实现选择和限制。

这是一个关于如何使用的单元测试:

https://code.google.com/p/spf4j/source/browse/trunk/spf4j-core/src/test/java/org/spf4j/concurrent/UIDGeneratorTest.java

【讨论】:

    【解决方案2】:

    如果它需要在每台 PC 上唯一:您可以使用 (System.currentTimeMillis() << 4) | (staticCounter++ & 15) 或类似的东西。

    这将允许您每毫秒生成 16 个。如果您需要更多,请移动 5,然后移动 31...

    如果它需要在多台 PC 中是唯一的,您还应该在主网卡的 MAC 地址中组合。

    编辑:澄清

    private static int staticCounter=0;
    private final int nBits=4;
    public long getUnique() {
        return (currentTimeMillis() << nBits) | (staticCounter++ & 2^nBits-1);
    }
    

    并将 nBits 更改为您每毫秒需要生成的最大数的平方根。

    它最终会翻身。可能需要 20 年左右,而 nBits 为 4。

    【讨论】:

    • 这是一个聪明的方法。我想我会相信 UUID 类,因为@smiller 让我更加相信它“足够独特”。
    • 我的公司使用与此非常相似的系统来生成我们的“UUID”。它工作正常(我从未见过重复的)。不过,它看起来真的很 hacky,而且它还可以让你弄清楚某些东西是在何时何地创建的。
    • 我把自己限制在很长的时间里,这很奇怪。如果您只是使用两个长整数并在同步方法中将计数附加到 currentTime,除非您的时钟更改,否则它不会失败。如果您对此感到担心,那么修复也很简单。
    【解决方案3】:

    为什么不这样做

    String id = Long.toString(System.currentTimeMillis()) + 
        (new Random()).nextInt(1000) + 
        (new Random()).nextInt(1000);
    

    【讨论】:

    • 为什么要创建 2 个新的 Random 对象?
    【解决方案4】:
    public class UniqueID {
        private static long startTime = System.currentTimeMillis();
        private static long id;
    
        public static synchronized String getUniqueID() {
            return "id." + startTime + "." + id++;
        }
    }
    

    【讨论】:

    • 不错的简单解决方案,是的。 UUID 位代码可能是我的应用程序中调用最多的代码,同时被多个线程调用,因此同步开销/瓶颈可能太多。
    • 不要滚动你自己的 UUID 代码。有很多微妙的方法可以让它出错。如果多个进程在同一毫秒内启动,例如当您启动一批机器同时运行同一任务时,此代码将做坏事。
    【解决方案5】:

    从内存中,RMI 远程包包含一个 UUID 生成器。我不知道这是否值得研究。

    当我必须生成它们时,我通常使用当前日期时间、用户名和计算机 IP 地址的 MD5 哈希和。基本上,这个想法是获取您能找到的有关计算机/人的所有信息,然后生成此信息的 MD5 哈希。

    它工作得非常好,而且速度非常快(一旦你第一次初始化 MessageDigest)。

    【讨论】:

      【解决方案6】:

      相当肯定 UUID 已经“足够好”了。有 340,282,366,920,938,463,463,374,607,431,770,000,000 个 UUID 可用。

      http://www.wilybeagle.com/guid_store/guid_explain.htm

      “从这些数字来看,一个人每年被陨石击中的风险估计为 170 亿分之一,这意味着概率约为 0.00000000006 (6 × 10−11),相当于一年创建几十万亿个 UUID 并有一个副本,也就是说,在接下来的 100 年每秒生成 10 亿个 UUID 之后,仅创建一个副本的概率大约为 50%。如果地球上每个人都拥有 6 亿个 UUID,那么一个副本将占 50% 左右”

      http://en.wikipedia.org/wiki/Universally_Unique_Identifier

      【讨论】:

      • 不错的参考!因此可以安全地推断,如果我在我的应用程序中使用 UUID.randomUUID(),那么它两次生成相同 UUID 的机会非常小......?
      • 好吧,仅仅因为有这么多可能的值,并不一定意味着他们将算法编写得足够好以获得良好的随机分布。话又说回来,UUID 类的设计者可能比我在一个下午的时间里花了更多的心思!
      • 是的,花了很多心思...“由开放软件基金会 (OSF) 标准化,作为分布式计算环境 (DCE) 的一部分”
      • 当你在晚上担心那个重复的 UUID 时,一定要留意那个陨石...... ;)
      • 最好使用Version 1 UUID,使用MAC地址+当前时间+随机数。与 Java 捆绑的 java.util.UUID 类不会生成版本 1,大概是出于安全和隐私问题。 UUID Implementations 上的 Wikipedia 页面列出了 2 个生成版本 1 的库。但如果使用“加密强”随机化器(如捆绑类)生成,完全随机(v4)通常就足够了。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-08-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多