不使用数据库生成主键

6

最近我遇到了一个问题,需要在“没有使用数据库的5个应用服务器的集群环境中生成主键 - [OAS版本10]”。

通常我们通过DB序列来生成PK,或者将值存储在数据库表中,然后使用SP来生成新的PK值...但是当前的要求是在不引用数据库的情况下使用JDK 1.4为我的应用程序生成主键。

需要专家的帮助,以找到更好的处理方法。

谢谢,


3
您需要一个数值型的主键还是基于字符串的也可以? - Timo Westkämper
只是好奇...您能简要解释一下,为什么不能使用数据库进行主键生成吗? - Kirill Leontev
数据类型没有限制。字符串也可以使用。 - Vicky
10个回答

9
使用UUID作为主键,并在客户端生成它。

编辑:
自从您的评论以来,我觉得我应该更详细地解释一下为什么这是一个好的做法。

虽然序列主键是数据库中最常见的,但对于分布式数据库或(特别是)支持“断开连接”用户界面的数据库,使用随机生成的主键通常是最佳选择。即用户不会始终与数据库保持连接的UI。

UUID是最好的随机生成密钥形式,因为它们保证非常独特; 生成相同UUID的可能性非常低,几乎完全不可能。 UUID也是无处不在的; 几乎每个平台都内置了生成它们的支持,对于那些没有的平台,几乎总会有第三方库来弥补空缺。

使用随机生成的主键的最大好处是,您可以在客户端上构建许多复杂的数据关系(具有主键和外键),并且(例如当您准备保存时)只需将所有内容转储到数据库中,而无需依赖于后插入步骤以获取稍后关系插入的密钥。

缺点是UUID是16字节而不是标准的4字节int - 空间增加了4倍。这在今天真的是个问题吗?我认为不是,但我知道有些人会持不同意见。当涉及到UUID时,唯一真正的性能问题是索引,特别是聚集索引。我将进入SQL Server世界,因为我并不经常针对Oracle进行开发,这是我的当前舒适区域,并且谈论SQL Server将默认创建跨表主键的所有字段上的聚集索引。这在自动增量int世界中运作得相当好,并且为基于密钥的查找提供了良好的性能。然而,任何值得他的盐的DBA都将以不同的方式聚类,但是那些不注意聚类且还使用UUID(Microsoft世界中的GUID)的人倾向于在插入重的数据库上遇到一些难以处理的减速,因为聚集索引必须重新计算每个插入,如果它针对UUID进行聚集,则新密钥可能放置在聚集序列的中间,可能需要重新排列大量数据以维护聚集索引。这在Oracle世界中可能是问题,也可能不是问题 - 我只是不知道Oracle PK是否像SQL Server中那样默认聚类。

如果那个长长的句子太难理解了,那么请记住:如果您将UUID用作主键,请勿对该密钥进行聚类!


它似乎适用于JDK 1.5,而我的要求是JDK 1.4。 - Vicky
@apx1sharma:啊,我完全忽略了你问题中的限制。这里有两个第三方库可以解决问题:JUGJohann Burkard's UUID library - Randolpho

3

您可能会发现查找UUID生成很有帮助。

在简单的情况下,每台机器上运行一个程序的一个线程,您可以执行以下操作:

MAC address + time in nanseconds since 1970.

2

如果你完全无法使用数据库,GUID/UUID是唯一可靠的方法。但是,如果偶尔可以使用数据库,可以尝试使用HiLo算法


正如我之前提到的“Radolpho”一样,UUID类适用于JDK 5,但是我正在寻找JDK 4中的解决方案。 - Vicky

2

您应该考虑使用UUID作为ID。Java5有一个来表示它们(还必须有一个工厂来生成它们)。通过这个工厂类,您可以将代码回溯到过时的Java 1.4,以获得所需的标识符。


2
请看Hibernate使用的这些策略(链接中的第5.1.5节)。其中解释了多种方法,它们的优缺点,并指出它们在集群环境中是否安全。最重要的是,已经有可用的代码为您实现 :)

这些方法需要涉及数据库,但我正在寻找一种不涉及数据库的方法。 - Vicky
1
你可以选择UUID算法并重复使用该算法(在同一部分解释):UUID包含:IP地址、准确到四分之一秒的JVM启动时间、系统时间和JVM内唯一的计数器值。无法从Java代码中获取MAC地址或内存地址,因此这是最佳选项,而不使用JNI。 - Sebastian

0
这是在MongoDB中的实现方法:http://www.mongodb.org/display/DOCS/Object+IDs 它们包括一个时间戳。
但你也可以安装Oracle Express并选择序列,你可以批量选择:
SQL> select mysequence.nextval from dual connect by level < 20;

NEXTVAL

     1
     2
     3
     4
     5
    ..  
    20

为什么不允许使用数据库?是因为钱的原因(Oracle Express是免费的),还是出于单点故障的担忧?或者您想在未来支持除了Oracle之外的其他数据库?


0
如果适用于您的应用程序,您可以使用较大的字符串键与UUID()函数或SHA1(随机数据)相结合。
对于连续的int,我会留给另一个帖子。

请您详细说明一下吗?UUID是JDK5的一部分,但我正在寻找JDK 4中的解决方案。我不明白,您打算如何利用SHA1? - Vicky
SHA1会对数据进行哈希处理,为相同的数据提供一致的键,并且具有非常低的碰撞可能性。这将成为您的唯一标识符。 - Rob Elsner

0

您可以基于以下三个组合生成密钥:

  1. 机器的IP地址或MAC地址
  2. 当前时间
  3. 每个实例上的递增计数器(以确保在同一台机器上不会生成相同的密钥,因为由于底层时间精度问题,在两个相邻的密钥创建中时间可能看起来相同)

是的。我认为,我们可以在每个主机上存储一个文件来实现您建议的第三点。可以同步访问该文件以进一步防止并发访问,以便每个线程只能访问一次数字,并在从方法返回之前递增该数字。 - Vicky

0
通过使用 Statement 对象,您可以调用 statement.getGeneratedKeys() 方法来检索由该 Statement 对象执行生成的自动生成键。

Java doc


0

它已经在许多基于Spring的应用程序中默认提供,例如Hybris-
typeCode 是您表格的名称,例如UserAddress等。

private PK generatePkForCode(final String typeCode)
    {
        final TypeInfoMap persistenceInfo = Registry.getCurrentTenant().getPersistenceManager().getPersistenceInfo(typeCode);
        return PK.createCounterPK(persistenceInfo.getItemTypeCode());
    }

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