在Java中生成全局唯一标识符

13

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

细节:

  1. 我没有使用RDBMS,因此我没有任何高级序列生成器(如Oracle提供的)
  2. 最好是快速的,最好全部在内存中 - 我不想打开文件并递增某个值
  3. 它需要是线程安全的(我预计一次只有一个JVM需要生成ID)
  4. 必须在JVM的实例之间保持一致。如果服务器关闭并重新启动,则ID生成器不应重新生成先前实例化中生成的相同ID(或者至少机会非常小 - 我预计有许多百万个永久资源)
  5. 我已经看到了EJB唯一ID模式文章中的示例。它们对我不起作用(我不想仅仅依赖System.currentTimeMillis(),因为我们将每毫秒持久化多个资源)。
  6. 我已经查看了此问题中提出的答案。我对它们的担忧是,随着时间的推移,我会得到重复的ID几率有多大?我被使用java.util.UUID生成UUID的建议所吸引,但是再次强调,获取重复的机会必须非常小。
  7. 我正在使用JDK6

你是否在不同的机器上运行多个应用程序实例?如果是这样,你是否可能批量启动机器,以便多个进程可能在同一毫秒内启动?如果攻击者找到一种方法来引起UUID冲突,那么这会危及你的应用程序安全吗? - Mike Samuel
(A)将生成多少ID?速度快吗?(每秒/分钟多少个) (B)是的,UUID正是为了您的目的而发明的。 - Basil Bourque
6个回答

33

我相信UUID已经"足够好了"。现在可以使用340,282,366,920,938,463,463,374,607,431,770,000,000个UUID。

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

"为了更好地理解这些数字,一个人每年被陨石击中的风险估计为1/170亿,这意味着概率约为0.00000000006(6×10^-11),相当于一年内创建数万亿个UUID并且有一个重复的概率。换句话说,在未来100年内每秒生成10亿个UUID,仅有1次重复产生的概率仅为50%。如果全球每个人都拥有6亿个UUID,则产生1个重复的概率也是50%。"

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


好的参考!那么如果我在我的应用程序中使用UUID.randomUUID(),它生成相同的UUID的机会是微乎其微的,对吧? - Julie
1
好的,仅仅因为有这么多可能的值,并不意味着他们编写的算法足够好以获得良好的随机分布。不过,UUID类的设计者可能比我在一个下午里想得更多! - Julie
是的,这方面考虑得相当多... "作为分布式计算环境(DCE)的一部分,经过开放软件基金会(OSF)标准化" - Shawn Miller
21
当你在夜晚担心重复的UUID时,务必要睁大眼睛留意那颗陨石... ;) - Michael Burr
首选使用版本1 UUID,使用MAC地址+当前时间+随机数生成。Java附带的java.util.UUID类不会生成版本1,可能是因为安全和隐私问题。UUID的维基百科页面实现列出了两个生成版本1的库。但是,如果使用“密码学强”的随机数生成器(如捆绑类中所示),则完全随机(v4)通常已足够好。 - Basil Bourque

1
如果需要每台电脑唯一:您可以使用 (System.currentTimeMillis() << 4) | (staticCounter++ & 15) 或类似的方法。

这将允许您每毫秒生成16个。 如果需要更多,请向左移5位并与31进行and运算...

如果需要跨多台计算机唯一,您还应该结合主网络适配器的MAC地址。

编辑:澄清说明

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

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

它最终会翻转。如果nBits为4,则可能需要20年左右。


1
这是一个聪明的方式。我想我会相信UUID类,因为@smiller给了我足够的信心它是“足够独特”的。 - Julie
我们公司使用一种非常类似的系统来生成我们的“UUIDs”。它运行良好(我从未见过重复)。但它看起来非常不专业,而且它允许你找出某个东西是在何时何地创建的。 - rmeador
这种方法有点hacky,因为它限制了我只能使用long类型。如果你使用两个long类型并在一个同步的方法中将计数附加到currentTime,则不会出现错误,除非时钟发生更改。如果你担心这一点,也很容易解决。 - Bill K

1
public class UniqueID {
    private static long startTime = System.currentTimeMillis();
    private static long id;

    public static synchronized String getUniqueID() {
        return "id." + startTime + "." + id++;
    }
}

1
不错且简单的解决方案。UUID 代码段可能会在我的应用程序中被许多线程同时调用,因此同步开销/瓶颈可能太大了。 - Julie
3
不要自己编写UUID代码。有很多微妙的方式会出错。如果多个进程在同一毫秒内启动,例如同时启动了一批运行相同任务的机器,则此代码将执行错误操作。 - Mike Samuel

0

为什么不这样做呢?

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

2
你为什么要创建两个新的随机对象? - user unknown

0

0

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

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

它非常有效,并且速度非常快(一旦您第一次初始化了MessageDigest)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接