【问题标题】:Hibernate IDENTITY vs SEQUENCE entity identifier generatorsHibernate IDENTITY vs SEQUENCE 实体标识符生成器
【发布时间】:2013-07-20 18:20:17
【问题描述】:

这个article 说:

与identity不同,列值的下一个数字将从内存中检索,而不是从磁盘中——这使得Sequence比Identity快得多

这是否意味着在身份的情况下ID来自磁盘?如果是,那么是哪个磁盘以及如何

使用序列,我可以在日志中看到插入新记录时对 DB 的额外选择查询。但是在身份的情况下,我没有在日志中找到额外的选择查询。 那么序列如何变得比身份更快

【问题讨论】:

    标签: java performance hibernate jpa jakarta-ee


    【解决方案1】:

    序列使用的策略:

    在插入新行之前,向数据库询问下一个序列值,然后以返回的序列值作为 ID 插入这一行。

    身份使用的策略:

    在不指定 ID 值的情况下插入一行。插入行后,向数据库询问最后生成的 ID。

    因此在这两种情况下查询的数量是相同的。 但是,Hibernate 默认使用对序列生成器更有效的策略。事实上,当它请求下一个序列值时,它会在内存中保留 50 个(即默认值,IIRC,它是可配置的)下一个值,并将这 50 个下一个值用于接下来的 50 次插入。只有在 50 次插入后,它才会进入数据库以获取 50 个下一个值。这极大地减少了自动生成 ID 所需的 SQL 查询数量。

    身份策略不允许这样的优化。

    【讨论】:

    • 我还要补充一点,SQL Server 在其缓存中“保留”了一批序列号(因此 内存)。默认情况下它是 50(这可能也是 NH 开发人员为他们的目的选择这个值的原因)。当这 50 个号码被使用时,SQL Server 需要更新其系统表,这些表以给定的顺序存储上次使用的号码,以便“保留”下一批号码 - 这是一个 disk 操作。身份也被缓存,但缓存大小缩小了 5 倍(计数 10 个数字),因此“序列生成器”更频繁地接触磁盘。
    • @JB,谢谢。我想这就是我一直在寻找的。但是在身份的情况下,“向数据库询问最后生成的 id”是什么意思,是不是像自动增量。我可以说还有反向的数据传输,从数据库到应用程序(往返),在身份的情况下,它只是从应用程序到数据库(单向)在序列的情况下(假设我们没有' t 为序列定义了 allocationSize)。可能我很困惑,请纠正。我相信,如果没有 allocationSize,每个 db 命中都会触发 Select 以获取序列。
    • 我不知道每个数据库使用的确切低级机制。但是对于我使用的那些,需要额外往返数据库才能获得最后一个自动生成的 ID。
    【解决方案2】:

    IDENTITY 生成器将始终需要数据库命中来获取主键值,而无需等待刷新以将当前实体状态转换与数据库同步。

    所以IDENTITY 生成器不能很好地与 Hibernate 后写一级缓存策略配合使用,因此 IDENTITY 生成器禁用了 JDBC 批处理。

    序列生成器可以从数据库值预分配中受益,您甚至可以采用hi/lo 优化策略。

    在我看来,最好的生成器是pooledpooled-lo 序列生成器。这些生成器将批处理友好的序列生成器与客户端值生成优化相结合,该优化与其他可能在不了解我们的生成策略的情况下插入行的 DB 客户端兼容。

    无论如何,您永远不应该选择TABLE 生成器,因为它的性能非常糟糕。

    【讨论】:

    • 带有 allocationSize 配置的表生成器怎么样?也很糟糕吗?
    • 不,我还没有。我要使用极光,它不支持序列。所以我研究了表格序列...感谢您的文章。
    【解决方案3】:

    虽然我个人是 Hibernate 的新手,但据我回忆,使用 Identity 基本上意味着 Hibernate 将检查您的数据库中下一个可能的 id 值是什么,并为它保留一个值。

    对于序列,您基本上告诉 Hibernate 根据您提供的特定序列生成下一个值。所以它必须通过查看下一个可能的 id 值来实际计算下一个 id。因此,触发了额外的查询。

    【讨论】:

      【解决方案4】:

      也许这会回答你的问题:

      与标识列值不同,标识列值是在行被生成时生成的 插入,应用程序可以获取之前的下一个序列号 通过调用 NEXT VALUE FOR 函数插入行。序列 调用 NEXT VALUE FOR 时分配编号,即使编号 永远不会插入到表中。 NEXT VALUE FOR 函数可以是 用作表定义中列的默认值。采用 sp_sequence_get_range 获取多个序列号的范围 一次。

      你可以找到详情here

      Identity 不需要额外的选择查询,因为 Identity 依赖于表,而 Sequence 独立于表,但正因为如此,我们甚至可以在创建行之前获取序列(当您执行 session.save(T entity) 时,序列是甚至在您提交事务之前生成)。

      序列: 您创建或更新实体->每次保存实体->休眠获取下一个序列值->您的程序在所有过程完成后返回值,无异常或回滚->您提交所有事务->休眠插入所有完整实体

      identity :提交事务时,插入不完整的实体(必须从身份列中获取)。所以序列的INSERT命令肯定比较慢,但好处是如果你取消插入,计数不会增加。

      【讨论】:

      • 谢谢。事实上,我也浏览了这些链接。如果身份不需要额外的选择查询,那么请你澄清一下序列是如何变得比身份更快的。
      • 当处于调试模式并使用具有其 ID 序列的实体时,我保存(在提交之前)一个实体,并且该实体甚至在数据库中存在该行之前就已经具有 ID。
      猜你喜欢
      • 1970-01-01
      • 2012-04-14
      • 2015-02-26
      • 1970-01-01
      • 2015-03-29
      • 2012-09-03
      • 1970-01-01
      • 2014-05-28
      • 1970-01-01
      相关资源
      最近更新 更多