【问题标题】:Can I put a MAX value for the database table primary key?我可以为数据库表主键设置一个 MAX 值吗?
【发布时间】:2012-04-18 15:10:08
【问题描述】:

我可以通过 JPA 或在数据库级别为数据库表主键设置一个 MAX 值吗?如果不可能,那我在考虑

  1. 在 0-9999999999 之间创建一个随机 key(9999999999 是我的最大值)
  2. 用新创建的key对数据库进行SELECT,如果返回对象是null,则INSERT,如果不重复返回步骤1

因此,如果我执行上述操作,有两个问题。请记住,环境是高并发的:

Q1:使用 SELECT(如果不存在)进行检查的开销是否显着?我真正的意思是:这个过程正常吗,因为通常我让DB为我创建一个独特的PK?

Q2:如果 Q1 没有造成显着的性能下降,我会遇到并发问题吗?例如,如果 P1 用 Id1 查表,Id1 不存在,它准备插入,P2 在 P1 可以之前偷偷插入 Id1。所以当 P1 插入 Id1 时,它会失败。我不希望这个过程在这里失败,我希望它回到循环,找到一个新的 id,重复这个过程。我该怎么做?

我的环境是 SQL 和 MYSQL db。我使用 JPA 和 Eclipselink 实现

注意:有些人质疑我以这种方式实现它的决定,答案正是TravisJ 下面的建议。我有一个非常高的并发环境。当一个进程启动时,我需要向另一个进程创建一个请求,向该进程传递一个唯一的 10 个字符长的 ID。由于环境是大电流,我想利用 PK 的独特而非空特性。请求中包含大量信息,因此我创建了一个Request 表,请求 ID 作为我的 PK。我知道,由于所有数据库都索引他们的 PK,因此查询出 PK 的速度很快。如果有更好的方法,请告诉我。

【问题讨论】:

  • 你为什么要做这样的事情?
  • 几乎不存在没有你想要的理由。看起来你在滥用PK的角色,这表明你做错了什么。长话短说:不要这样做。
  • 我不同意没有理由。这可以用来为主键创建某种散列。提出的主要问题似乎是竞争条件,这意味着不同的进程需要一个信号量。

标签: java mysql sql jpa eclipselink


【解决方案1】:

看,

http://en.wikibooks.org/wiki/Java_Persistence/Identity_and_Sequencing

和,

http://en.wikibooks.org/wiki/Java_Persistence/Identity_and_Sequencing#Advanced_Sequencing

JPA 已经有很好的 Id 生成支持,实现你自己的没有意义。

如果您关心并发性和性能,并且使用 MySQL,我建议您使用具有较大预分配大小的 TABLE 生成器(在其他数据库上,我建议使用 SEQUENCE 生成器)。如果您有大量数据,请确保为您的 id 使用 long。

如果您真的认为您需要的不止这些,那么请考虑生成 UUID id。 EclipseLink 2.4 提供了一个@UUIDGenerator。

【讨论】:

  • 这正是我想要的。但是,我对此还有其他几个问题。我的数据库是Microsoft SQL server,我认为不支持@SequenceGenerator,所以我必须使用@TableGenerator,所以如果你可以看看这篇文章,我有几个问题,stackoverflow.com/questions/10033727/…。非常感谢
【解决方案2】:

为什么不从 1 开始并使用自动增量?这将更有效率,因为您不会遇到必须循环通过的碰撞。如果你的数字用完了,无论哪种方式,你都会在同一条船上,但至少按顺序进行,你不必处理冲突。

想象一下,当您用完 90% 的可用号码时,您试图找到一个未使用的密钥。这将需要一些时间,而且如果您随机生成它们,它总是有可能(在您的一生中)永远找不到未使用的密钥。

此外,使用自动增量,很容易判断您是否接近限制 (SELECT MAX(col))。您可以编写警报脚本,让您知道何时需要重置。对于随机方法,该查询是什么样的?

如果您使用的是 InnoDB,那么您可能仍然不想使用主键。将随机记录插入聚集索引会影响性能,因为必须对实际表数据进行重新排序。而是使用唯一键(使用自增主键)。

使用相关列的唯一索引,只需在范围内生成一个随机数并尝试插入它。如果插入失败,则生成一个新号码并重试。如果插入成功,则继续。这解决了并发问题。

不过,顺序自动递增键会产生更好的性能。

【讨论】:

  • 问题是我们计划每三个月清理一次数据库。因此,正在从数据库中删除密钥。因此,在自动增量密钥中,数据库密钥是否会回绕,假设它处于 MAX(9999999999),但 0 到 50000000 之间的密钥现在是免费的,因为我们从数据库中删除了,数据库是否知道包装下一个插入的关键点?
  • @ThangPham,要在擦除数据库后再次从 1 开始自动递增列,只需执行 ALTER TABLE tbl AUTO_INCREMENT = 1。
【解决方案3】:

您可以在表定义中实现检查约束:

CREATE TABLE P
(
P_Id int PRIMARY KEY NOT NULL,
...
CONSTRAINT chk_P_Id CHECK (P_Id>0 and P_Id<9999999999)
)

编辑: 如 cmets 所述,MySql 不遵守 CHECK 约束。这是错误日志中存在 6 年的缺陷,MySql 团队尚未修复它。由于 MySql 现在由 Oracle 公司监管,它可能永远不会被修复(简单地认为是“记录的限制”,不喜欢它的人可以升级到付费的 DBMS)。但是,这种语法和检查约束特性本身在 Oracle、MS SQL Server(包括 SQLExpress/MSDE)、MS Access、Postgre 和 SQLite 中都可以工作。

【讨论】:

  • 这种语法肯定适用于 MySql、Oracle、MSS 和 MS Access。 w3schools.com/sql/sql_check.asp ALTER TABLE 的相关语法也适用于所有这些数据库。我不能代表 SQLite 或 PostgreSQL。
  • 检查约束do not work in MySQL。他们默默地失败了。
  • 编辑:现在我可以为 Postgre 和 SQLite 说话了,它也可以在那里工作。
  • @Ben:哇。 MySql 允许您定义它从不检查的东西,这非常奇怪。这会让开发人员感到 15 种困惑。
  • @KeithS,已经详细讨论过all overSO 和DBA.se。我还没有找到任何不同意你的人!
猜你喜欢
  • 2011-02-05
  • 1970-01-01
  • 2011-01-31
  • 1970-01-01
  • 2011-09-17
  • 2012-12-29
  • 1970-01-01
  • 2018-06-23
  • 2014-01-11
相关资源
最近更新 更多