Spring Data JPA在Oracle中使用@SequenceGenerator跳号是因Hibernate默认allocationSize=50预取ID缓存,进程终止或回滚即丢失未用ID;需合理设置allocationSize与Oracle序列CACHE值对齐,避免双缓存放大跳号。
spring data jpa 在 oracle 上用 @sequencegenerator 时跳号,不是配置漏了、也不是代码写错了,而是 hibernate 默认预取 50 个 id 缓存在内存里——只要应用重启、事务回滚、甚至只是服务被 kill -9,没用完的那些就永远丢弃。这不是 bug,是设计行为。
为什么 allocationSize = 50 是默认值却最危险
Hibernate 的 allocationSize 控制每次从 Oracle 序列取多少个值缓存到 JVM 内存中。默认值 50 意味着:调一次 NEXTVAL,Oracle 返回 1 个起始值,Hibernate 自己加 1、2、3…生成后续 49 个 ID。一旦进程终止,这 49 个未分配的 ID 就彻底消失。
- 哪怕你只插入了 3 条记录,下次启动后第一个 ID 可能直接跳到 51
- 测试环境频繁启停,跳号会特别明显;生产环境若偶发宕机,也可能突然跳几百
-
allocationSize和 Oracle 序列的CACHE值无关,但二者叠加会让跳号幅度翻倍(比如序列CACHE 20+ JPAallocationSize=50,单次最大可能跳 1000)
怎么设 allocationSize 才合理
不能盲目设成 1,也不能死守 50。关键看你的业务对「连续性」和「性能」的容忍边界:
- 设为
1:每次插入都触发一次NEXTVAL查询,TPS 下降明显,适合审计强连续要求场景(如财务凭证号) - 设为
10或20:平衡点。跳号控制在个位或两位数,数据库 round-trip 增加有限 - 设为
50或更高:仅适用于日志类、埋点类等 ID 完全不对外暴露的表,且确认序列CACHE值已同步调大(避免双缓存放大跳号) - 别设非整数或负数:
allocationSize必须是正整数,否则启动报IllegalArgumentException
@SequenceGenerator 和 CREATE SEQUENCE 的 CACHE 要对齐
很多人只改 Java 层 allocationSize,忘了 Oracle 序列本身的 CACHE 设置。两者不匹配会导致跳号不可预测:
- Oracle 序列建表语句必须显式含
CACHE N,例如:CREATE SEQUENCE user_id_seq START WITH 1 INCREMENT BY 1 CACHE 20 - 如果 JPA 设
allocationSize=20,但 Oracle 序列是NOCACHE,每次插入都会触发硬解析+重做日志写入,性能骤降 - 如果 JPA 设
allocationSize=1,但 Oracle 序列是CACHE 100,那跳号仍可能发生(实例崩溃时丢掉整个 cache 段) - 推荐组合:
allocationSize=20+CACHE 20,或allocationSize=1+NOCACHE,避免错层缓存
跳号无法彻底消除,但可以隔离影响面
无论你怎么调 allocationSize,只要用了 Oracle 序列,就逃不开事务回滚、实例异常终止、FLUSH SHARED_POOL 这三类跳号根源。真正要做的不是消灭跳号,而是让它不干扰业务:
- 主键 ID 永远只用于关联和索引,禁止在前端展示、禁止人工对账、禁止进 Excel 表头编号
- 对外暴露的「单据号」「订单号」必须由应用层生成(比如时间戳+机器码+自增序号),和数据库主键物理分离
- 审计需求写进 SLA 明确注明:“SEQUENCE ID 允许跳号,不视为数据异常”,省去后期扯皮
- 监控
DBA_SEQUENCES.CACHE_SIZE和实际插入 ID 差值,若发现平均跳号 >CACHE_SIZE × 2,说明有实例频繁崩溃,该查 OS 日志了











