Spring Boot 3.3.0 搭配MySQL 8出现异常自增问题
Spring Data MySQL中user_seq序列仅前两次自增的原因分析
问题场景
- 使用Spring官方示例项目
gs-accessing-data-mysql-complete - 通过Postman调用接口插入User对象:
http://localhost:8080/demo/add?name=paco&email=paco@gmail.com - 现象:
user_seq表仅前两次插入时每次自增50,后续插入不再触发序列的查询/更新操作,直接完成数据插入 - 对应Hibernate执行日志:
Hibernate: select next_val as id_val from user_seq for update Hibernate: update user_seq set next_val= ? where next_val=? Hibernate: insert into user (email,name,id) values (?,?,?) Hibernate: select next_val as id_val from user_seq for update Hibernate: update user_seq set next_val= ? where next_val=? Hibernate: insert into user (email,name,id) values (?,?,?) Hibernate: insert into user (email,name,id) values (?,?,?) Hibernate: insert into user (email,name,id) values (?,?,?) - 接口实现代码:
@PostMapping(path="/add") // Map ONLY POST Requests public @ResponseBody String addNewUser (@RequestParam String name , @RequestParam String email) { // @ResponseBody means the returned String is the response, not a view name // @RequestParam means it is a parameter from the GET or POST request User n = new User(); n.setName(name); n.setEmail(email); userRepository.save(n); return "Saved"; }
原因解析
这是Hibernate的序列ID预分配优化策略导致的,具体逻辑:
- MySQL本身不支持原生数据库序列(Sequence),当Hibernate使用
GenerationType.AUTO或GenerationType.SEQUENCE作为ID生成策略时,会自动创建user_seq表模拟序列行为,以此实现跨数据库的ID生成兼容性。 - 为了减少数据库交互提升性能,Hibernate默认会一次性预分配50个ID(由
allocationSize参数控制,默认值为50):- 第一次插入时,Hibernate查询
user_seq获取当前序列值,将序列值直接增加50,然后使用预分配批次中的第一个ID完成插入。 - 若第二次插入处于新的会话或应用重启场景,之前预分配的ID无法复用,Hibernate会再次查询
user_seq获取当前值,更新序列值+50,预分配新的ID批次。 - 后续插入会直接使用预分配好的剩余ID,无需再操作
user_seq表,因此不会再触发序列的查询和更新。
- 第一次插入时,Hibernate查询
user_seq机制是否半废弃?
并没有半废弃,它是Hibernate为跨数据库兼容设计的序列模拟方案:
- 当使用
GenerationType.SEQUENCE策略时,Hibernate会根据数据库类型自动适配:在PostgreSQL、Oracle等支持原生序列的数据库中直接使用原生序列;在MySQL这类不支持的数据库中,就会通过user_seq这类表模拟序列。 - 如果更倾向于使用MySQL原生自增特性,可以将ID生成策略改为
GenerationType.IDENTITY,此时Hibernate会直接依赖MySQL的AUTO_INCREMENT,不再创建和使用user_seq表。
内容的提问来源于stack exchange,提问作者Raul Lapeira Herrero
相关产品推荐
相关产品推荐

