hibernate oracle 序列产生大的间隙

Posted

技术标签:

【中文标题】hibernate oracle 序列产生大的间隙【英文标题】:hibernate oracle sequence produces large gap 【发布时间】:2011-07-17 19:09:18 【问题描述】:

我使用的是休眠 3,oracle 10g。我有一张桌子:主题。定义在这里

CREATE TABLE SUBJECT
    ( 
     SUBJECT_ID NUMBER (10), 
     FNAME VARCHAR2(30)  not null, 
     LNAME VARCHAR2(30)  not null, 
     EMAILADR VARCHAR2 (40),
     BIRTHDT  DATE       not null,
     constraint pk_sub primary key(subject_id) USING INDEX TABLESPACE data_index
    ) 
;

插入新主题时,sub_seq用于创建主题id,定义在这里

create sequence sub_seq
       MINVALUE 1 
       MAXVALUE 999999999999999999999999999 
       START WITH 1
       INCREMENT BY 1 
       CACHE 100 
       NOCYCLE ;

Subject 类是这样的:

@Entity
@Table(name="ktbs.syn_subject")
public class Subject 

    @Id 
    @Column(name="subject_id")
    @GeneratedValue(strategy=GenerationType.SEQUENCE, generator="SUB_SEQ")
    @SequenceGenerator(name="SUB_SEQ", sequenceName = "SUB_SEQ")
    private long subjectId;
    private String fname;
    private String lname;
    private String emailadr;
    private Date birthdt;

在主题表中,数据库中有 4555 个主题由 plsql 脚本从 excel 加载,并且 sub_sequence 工作正常。主题 ID 范围为 1--4555。

但是,当我使用 hibernate 从我的应用程序中添加主题时, 序列号跳到了255050。经过几天的运行,hibernate生成的主题id是这样的

270079
270078
270077
270076
270075
270074
270073
270072
270071
270070
270069
270068
270067
270066
270065
270064
270063
270062
270061
270060
270059
270058
270057
270056
270055
270054
270053
270052
270051
270050
265057
265056
265055
265054
265053
265052
265051
265050
260059
260058
260057
260056
260055
260054
260053
260052
260051
260050
255067
255066
255065
255064
255063
255062
255061
255060
255059
255058
255057
255056
255055
255054
255053
255052
255051
255050
4555
4554
4553
.
.
.
.
1

有几个较大的差距:4555 到 255051、255067 到 260051、265057 到 270051

这是一种浪费,而不是理想的行为。

有谁知道为什么会发生这种情况并热修复它

谢谢

【问题讨论】:

【参考方案1】:

如果您阅读以下链接,您会发现问题是由您的序列创建命令中的 CACHE 设置引起的。移除缓存设置会在一定程度上解决问题——但没有考虑回滚等的可能性。

链接是:http://asktom.oracle.com/pls/apex/f?p=100:11:0::::P11_QUESTION_ID:369390500346406705

现在重新同步序列的唯一方法是重新创建序列,重命名当前表并再次创建表,然后将旧表中的记录重新插入到新表中。

注意:序列的缓存值对于一次性分配“x”序列值的大负载很有用。如果您使用的事务系统一次只插入一个 - 那么缓存就没有用(或者我应该说 - 我从来没有发现它有用)。

注意:这是我对序列缓存选项的理解。您可以查看有关 CREATE SEQUENCE 命令的 Oracle 文档以获取更多信息。但是上面的链接应该可以为您的问题提供一个合理的答案。

谢谢。 保罗

【讨论】:

谢谢。但是gap是10000,定义的gap是100,是不是和hibernate设置有关? 每次插入时都会分配 100 个序列值。因此,10,000 的间隙仅插入 1,000 或 100 条记录。您需要阅读序列缓存以获取更多详细信息 - 但要点是,您根本不能依赖序列来维护一组顺序值,因为无论缓存值如何,间隙都将始终存在。 我只是从 sql developer 发出查询 select sub_seq.nextval from dual;它显示 5408。这意味着休眠不使用 oracle 序列对象 sub_seq。我怀疑休眠设置有问题 看我的回答。这是因为序列生成器使用了hilo。【参考方案2】:

我认为问题在于序列生成器并不是真正的序列生成器,而是序列 hilo 生成器,默认分配大小为 50。如文档所示:http://docs.jboss.org/hibernate/stable/annotations/reference/en/html_single/#entity-mapping-identifier

这意味着如果序列值为 5000,下一个生成的值将是 5000 * 50 = 250000。将序列的缓存值添加到等式中,它可能会解释你巨大的初始差距。

检查序列的值。它应该小于最后生成的标识符。注意不要将序列重新初始化为最后生成的值 + 1,因为生成的值会呈指数增长(我们遇到过这个问题,并且由于溢出而导致负整数 id)

【讨论】:

谢谢。是的,这可能是问题所在。所以在我的情况下休眠不使用oracle定义的序列对象?如何解决这个问题? 如果我记得正确设置时,您还会从重新启动中获得间隙,因为数据库中的序列是 1、2、3、4,如果分配大小是 500,那么当它获得下一个序列时,500 *1=500,当它最终到达 999 时,它调用 get next sequence 检索 2(如果另一个进程已经获得 2,则为 3),然后 500*2=1000 一直到 1499。服务器仅在使用 1 或500 中的 2 个值会导致差距。 哦,当你调用persist并且不刷新(如果在手动模式下)它会用完序列中的id,这些id也永远不会保存到数据库中(有很多原因,因为我刚刚注意到您没有使用 allocationSize,我们使用 ALOT 来防止插入操作中的持续数据库交互,因为它是更多的往返工作)【参考方案3】:

同意 JB。但仍然感谢 PaulJ。

下面是我的注释代码更具体:

@Entity
@Table(name="ktbs.syn_subject")
public class Subject 

  @Id 
  @Column(name="subject_id")
  @GeneratedValue(strategy=GenerationType.SEQUENCE, generator="SUB_SEQ")
  @javax.persistence.SequenceGenerator(name="SUB_SEQ", sequenceName = "SUB_SEQ")
  private long subjectId;
  private String fname;
  private String lname;
  private String emailadr;
  private Date birthdt;

如果你使用javax.persistence.SequenceGenerator,hibernate 使用 hilo 并且可能会在序列中产生很大的间隙。有一个帖子解决了这个问题: https://forum.hibernate.org/viewtopic.php?t=973682

有两种方法可以解决这个问题

    在SequenceGenerator注解中,添加allocationSize = 1, initialValue= 1

    不要使用 javax.persistence.SequenceGenerator,而是使用 org.hibernate.annotations,如下所示:

    @javax.persistence.SequenceGenerator(
        name = "Question_id_sequence", 
        sequenceName = "S_QUESTION"
    )
    
    @org.hibernate.annotations.GenericGenerator(
        name="Question_id_sequence", 
        strategy = "sequence", 
        parameters =  
            @Parameter(name="sequence", value="S_QUESTION") 
        
    )
    

我已经测试了这两种方法,效果很好。

【讨论】:

【参考方案4】:

另一种解决方案是:

使用 'GenerationType.AUTO' 而不是 'GenerationType.SEQUENCE' 作为 @GeneratedValue 的策略,如下所示;

@Id
@SequenceGenerator(name = "studentId", sequenceName = "student_Id")
@GeneratedValue(strategy = GenerationType.AUTO, generator="studentId")  
private int studentId;

【讨论】:

不适用于 Hibernate 4 和 JPA。设置 allocationSize= 1 即可。【参考方案5】:

最成功的答案是:

@Id
@SequenceGenerator (name = "id_sequence", sequenceName = "sq50")
@GeneratedValue(strategy = GenerationType.AUTO, generator = "id_sequence")
public int getId() 
return id;

【讨论】:

【参考方案6】:

如here 所说,尝试使用您的数据库序列INCREMENT BY 编号调整您的SequenceGenerator.allocationSize

【讨论】:

【参考方案7】:

如果您的序列INCREMENT VALUE 是 1 并且您不需要持久化很多实体,那么实际上分配大小 = 1 就可以了。 但是,如果您想保留数千或数百万条记录,上述设置可能会成为性能瓶颈,因为每次保存都需要获取 id,因此需要读取 db。

为了解决这个问题,我们需要将allocationSize 设置为 500 之类的值,并将 DB 中的序列 INCREMENT VALUE 也设置为 500,然后最重要的是添加一个休眠设置 hibernate.id.new_generator_mappings 以要求它使用新的序列生成器实现,这里我假设你在 java Config 类中设置你的休眠属性:

properties.setProperty("hibernate.id.new_generator_mappings", Boolean.toString(true));

这样,Hibernate 将使用 SequenceStyleGenerator 而不是旧的 SequenceHiLoGenerator 来生成 id。 SequenceStyleGenerator 对 jpa 和 oracle 更友好。它基于序列式数据库结构生成标识符值。变化范围从实际使用序列到使用表格来模拟序列。

如果您在同一条船上,请查看我的帖子以获取更多详细信息:

vcfvct.wordpress.com/2016/04/23/jpa-sequencegenerator-with-allocationsize-1-performance-tuning/

【讨论】:

【参考方案8】:

我也有类似的问题。序列生成器和序列 hilo 生成器非常相似,但有区别。在休眠 3 中,hilo 生成器与默认值 50 相乘。因此无需增加 DB 序列。另一方面,更高版本的 hibernate 默认使用序列生成器。因此需要将 DB 增量为 50。

https://access.redhat.com/documentation/en-US/JBoss_Enterprise_Application_Platform/6.3/html/Migration_Guide/Preserve_the_Existing_Behavior_of_the_Hibernate_Identity_Auto_Generated_Value1.html

我遇到了这个问题,它有多个休眠版本(3 和 5)。相同的配置工作正常(在 DB 中增加 1)。但是在休眠 5 中失败了。因此我更新了我的 persistence.xml 如下。这确保了 hilo 的生成

        <property name="hibernate.id.new_generator_mappings" value="false" />

【讨论】:

【参考方案9】:

对此的一种解决方案是,我们可以将具有 allocationSize 的序列生成器配置为:

@SequenceGenerator(name = "gen_name", sequenceName = "seq_name", allocationSize= 1)

【讨论】:

如果表增长缓慢,这还不够,可能还不够。如果 Oracle 的序列缓存设置为例如20,并且每周只添加〜1,并且每周将数据库关闭进行维护,相邻记录之间仍然会有19个ID的差距。要解决此问题,请将序列缓存大小设置为

以上是关于hibernate oracle 序列产生大的间隙的主要内容,如果未能解决你的问题,请参考以下文章

Oracle DB + Hibernate 中的奇怪序列顺序

如何使用现有的Oracle序列在hibernate中生成id?

用于从序列生成 id 的 Oracle 触发器的 HIbernate 问题

Hibernate 5 ID AUTO 生成类型为 Oracle 作为序列和 MySQL 作为身份

Hibernate + Oracle 创建自增序列ID

HDU 6047 Maximum Sequence (贪心+单调队列)