You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.20 03:12:44