升级Spring Boot后PostgreSQL序列nextval远小于表最大ID是什么原因?
问题根因
本质是Spring Boot版本升级带来的Hibernate主键生成策略行为变化,具体分三个阶段解释:
1. 升级后第一个报错的由来
- Spring Boot 1.5 默认集成的低版本Hibernate没有「实体allocationSize配置与数据库序列increment值一致性校验」逻辑,你没显式写
allocationSize时,默认值就是50,因为没有校验逻辑,所以旧版本运行正常。 - Spring Boot 2 集成的Hibernate 5.2+版本新增了上述校验,你的数据库里
X_SEQ序列实际的increment by配置是1,和实体默认的50不匹配,因此触发报错。
2. 序列nextval值远小于表最大ID的原因
Hibernate的GenerationType.SEQUENCE默认启用hi/lo高低位优化算法降低数据库访问频率:
- 当
allocationSize=50时,Hibernate每调用1次nextval('X_SEQ')拿到一个高位值,就可以在本地连续生成50个主键ID(计算规则:高位值 * allocationSize + 低位偏移量,偏移量从0到49循环),用完这50个ID才会再次查序列拿新的高位值。 - 旧版本运行期间一直用这套逻辑生成ID,所以数据库里
X_SEQ的当前值虽然只有2万多,但乘以50后生成的ID已经可以覆盖到千万级,和你表内的最大ID 18917000是匹配的。
3. 改allocationSize=1后报唯一键冲突的原因
你把allocationSize改成1之后,Hibernate会关闭hi/lo优化,每次插入都直接拿nextval('X_SEQ')的返回值当主键,但序列当前返回的2万多的ID,早就被之前的hi/lo算法生成过了,自然会触发唯一键冲突报错。
修复方案
你可以根据业务需求二选一:
- 方案一:保留
allocationSize=1的配置,直接调整数据库序列的起始值到比表最大ID更大即可,执行SQL:
ALTER SEQUENCE X_SEQ RESTART WITH 18917001;
- 方案二:想要保留hi/lo优化提升插入性能,就把数据库序列的
increment by值改成50,实体的allocationSize也保持默认的50即可,不需要调整序列当前值。
内容的提问来源于stack exchange,提问作者Idriss
相关产品推荐
相关产品推荐

