jOOQ DSL.using方法引发Java服务启动间歇性冻结问题排查
问题:Java服务启动间歇性冻结,DSL.using占用高CPU
现象
Java服务启动时出现间歇性冻结,重启后恢复正常。通过AWS CodeGuru排查发现:
- 2个线程在
DSL.using方法内占用68%的CPU - 另有1个线程处于阻塞状态
相关代码
执行DSL.using的业务方法
public void myMethod(final Instant now) { final int count = using(configuration) .update(MY_TABLE) .set(MY_TABLE.ACTIVE, true) .where(MY_TABLE.PAUSED_UNTIL.isNotNull()) .and(MY_TABLE.ACTIVE.isFalse()) .execute(); if (count > 0) { ... } }
Configuration初始化代码(类构造方法中)
DataSource dataSource = DataSourceFactory.provide(); Configuration configuration = new DefaultConfiguration() .derive(dataSource) .derive(SQLDialect.POSTGRES) .derive(EnumFixingExecuteListener::new);
自定义DataSourceFactory的provide方法
public HikariDataSource provide() { HikariConfig config = new HikariConfig(); config.setDataSourceClassName("org.postgresql.ds.PGSimpleDataSource"); config.addDataSourceProperty("serverName", postgresConfig.host); config.setMaximumPoolSize(6); config.addDataSourceProperty("databaseName", postgresConfig.database); config.addDataSourceProperty("user", postgresConfig.username); config.addDataSourceProperty("password", postgresConfig.password); HikariDataSource dataSource = new HikariDataSource(config); log.trace("Providing dataSource {}", dataSource); return dataSource; }
使用版本
- jOOQ 3.16.23(构建日期:2023-12-20T14:13:48Z)
- PostgreSQL 14.10
疑问
DSL.using方法为何会出现冻结?还有哪些可能的原因导致该现象?
可能的原因及分析
1. DSL.using重复初始化引发的资源消耗
每次调用myMethod都通过using(configuration)创建新的DSLContext实例,DSL.using内部会执行方言适配、监听器实例化等初始化操作。若该方法被高频调用,重复创建对象会引发CPU占用飙升,甚至线程竞争导致阻塞。
优化建议:将DSLContext实例化为类级单例,避免重复初始化:
// 类成员变量 private final DSLContext dslContext; // 构造方法中初始化 public YourClass() { DataSource dataSource = DataSourceFactory.provide(); Configuration configuration = new DefaultConfiguration() .derive(dataSource) .derive(SQLDialect.POSTGRES) .derive(EnumFixingExecuteListener::new); this.dslContext = DSL.using(configuration); } // 业务方法中直接复用实例 public void myMethod(final Instant now) { final int count = dslContext .update(MY_TABLE) .set(MY_TABLE.ACTIVE, true) .where(MY_TABLE.PAUSED_UNTIL.isNotNull()) .and(MY_TABLE.ACTIVE.isFalse()) .execute(); if (count > 0) { ... } }
2. Hikari连接池配置不合理
- 连接池最大容量设为6,若业务并发量超过该值,会导致线程等待获取连接,引发阻塞。
- 未配置连接超时,若数据库端响应缓慢(网络延迟、数据库负载高),线程会无限等待或重试,占用CPU。
优化建议:
- 监控数据库连接使用情况,根据实际并发量调整
maximumPoolSize - 增加超时配置避免无限等待:
config.setConnectionTimeout(3000); // 3秒连接超时 config.setIdleTimeout(60000); // 1分钟空闲超时 config.setMaxLifetime(1800000); // 30分钟连接生命周期
3. 自定义监听器的潜在问题
自定义的EnumFixingExecuteListener如果在初始化或执行逻辑中包含同步锁、耗时IO操作、循环逻辑,可能引发线程阻塞或CPU占用。检查该监听器的renderStart、executeStart等方法是否存在锁竞争或性能瓶颈。
4. 数据库端性能瓶颈
- PostgreSQL数据库负载过高(CPU、内存耗尽,锁等待),导致SQL执行缓慢,客户端线程阻塞。
- 应用与数据库之间的网络延迟、丢包,导致连接建立或SQL执行超时,客户端线程重试占用CPU。
排查建议:
- 在数据库端执行
SELECT * FROM pg_stat_activity查看连接状态,排查长时间运行的查询或锁等待 - 监控数据库服务器的CPU、内存、磁盘IO资源使用情况
5. jOOQ版本的已知缺陷
jOOQ 3.16.23属于较旧版本,可能存在DSL.using初始化时的性能或线程安全问题。查看jOOQ官方发布说明,确认是否有相关bug修复,考虑升级到3.17.x或3.18.x等稳定新版本。
内容的提问来源于stack exchange,提问作者DWilches
相关产品推荐
相关产品推荐

