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

HikariCP连接超时求助:Spring-MVC+PostgreSQL环境故障排查

问题排查与解决方案

我正在开发基于Spring-MVC的应用,采用HikariCP作为连接池,数据库为PostgreSQL(配置150个连接),HikariCP连接池大小设为15。偶尔会突发连接超时错误,该错误10-20秒后消失,但会拖慢整个服务器,且多数时候服务器并不繁忙。我已尝试参考相关方案并添加泄漏检测,但无效果。请问问题出在哪里,该如何解决?

错误日志

java.sql.SQLTimeoutException: Timeout after 30000ms of waiting for a connection. 
at com.zaxxer.hikari.pool.BaseHikariPool.getConnection(BaseHikariPool.java:233) 
at com.zaxxer.hikari.pool.BaseHikariPool.getConnection(BaseHikariPool.java:183) 
at com.zaxxer.hikari.HikariDataSource.getConnection(HikariDataSource.java:110)

root-context.xml配置

<beans:bean id="dataSource" class="com.zaxxer.hikari.HikariDataSource" destroy-method="close">
    <beans:property name="dataSourceClassName" value="org.postgresql.ds.PGSimpleDataSource"/>
    <beans:property name="minimumIdle" value="2"/>
    <beans:property name="maximumPoolSize" value="17" />
    <beans:property name="maxLifetime" value="300000" />
    <beans:property name="idleTimeout" value="25000" />
    <beans:property name="leakDetectionThreshold" value="3000"/>
    <beans:property name="dataSourceProperties">
        <beans:props>
            <beans:prop key="url">jdbc:postgresql://localhost:5432/DB_NAME</beans:prop>
            <beans:prop key="user">USERnamE</beans:prop>
            <beans:prop key="password">PASSWORD</beans:prop>
        </beans:props>
    </beans:property>
</beans:bean>
<!-- Hibernate 4 SessionFactory Bean definition -->
<beans:bean id="hibernate4AnnotatedSessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
    <beans:property name="dataSource" ref="dataSource"/>
    <beans:property name="packagesToScan" value="com.ourapp.spring.model"/>
    <beans:property name="hibernateProperties">
        <beans:props>
            <beans:prop key="hibernate.dialect">org.hibernate.dialect.PostgreSQL9Dialect</beans:prop>
            <beans:prop key="hibernate.show_sql">false</beans:prop>
            <beans:prop key="hibernate.jdbc.batch_size">50</beans:prop>
            <beans:prop key="hibernate.hbm2ddl.auto">update</beans:prop>
            <beans:prop key="cache.use_second_level_cache">true</beans:prop>
            <beans:prop key="cache.use_query_cache">true</beans:prop>
            <beans:prop key="hibernate.order_updates">true</beans:prop>
            <beans:prop key="show_sql">false</beans:prop>
        </beans:props>
    </beans:property>
</beans:bean>

更新:典型的保存与读取代码示例

@Repository
@Transactional
public class AppDetailsDAOImpl implements AppDetailsDAO {

    private final SessionFactory sessionFactory;

    @Autowired
    public AppDetailsDAOImpl(SessionFactory sessionFactory) {
        this.sessionFactory = sessionFactory;
    }

    @Override
    public Long saveAppDetails(AppDetails appDetails, int personid) {
        Session session = this.sessionFactory.getCurrentSession();
        Person person = (Person) session.get(Person.class, personid);
        if (person != null) {
            person.getAppDetailsSet().add(appDetails);
            appDetails.setUserApps(person);
            Long saveid = (Long) session.save(appDetails);
            session.flush();
            return saveid;
        }
        return (long) 0;
    }

    @Override
    public AppDetails getAppDetailsByAppId(Long appId) {
        Session session = this.sessionFactory.getCurrentSession();
        return (AppDetails) session.get(AppDetails.class, appId);
    }
}

这种突发连接超时、短时间自动恢复但拖慢服务的情况,我遇到过不少类似案例,结合你的配置和代码,核心问题大概率出在连接未及时释放或数据库端隐式阻塞上,下面分点给你拆解:

一、先排查连接泄漏的真实情况

你配置了leakDetectionThreshold=3000(3秒),但可能因为阈值太小,日志被大量误报淹没,或者没开启HikariCP的DEBUG日志导致错过关键信息:

  • 把leakDetectionThreshold调整到10000ms(10秒),过滤掉正常的短时间连接持有场景。
  • 将日志框架(Logback/Log4j)中com.zaxxer.hikari的级别设为DEBUG,这样能看到每个连接的获取、释放时间戳,以及真正的泄漏栈信息——如果有连接被持有超过10秒,Hikari会直接打印调用栈,帮你定位到具体哪段代码没释放连接。

二、事务与Session的生命周期问题

你的DAO用了@Transactional和getCurrentSession(),理论上Spring会自动管理Session和连接,但有几个细节容易出问题:

  1. 未处理的异常导致事务挂起:如果业务代码抛出未被捕获的异常,且没配置@Transactional(rollbackFor = Exception.class),默认只有RuntimeException才会触发回滚。检查异常处理逻辑,确保所有异常都能触发事务回滚——未回滚的事务会一直占用连接。
  2. 多余的手动flush()操作:你在saveAppDetails里手动调用了session.flush(),@Transactional方法结束时会自动提交事务并触发flush,手动flush会让连接提前进入"活跃"状态,若后续有非DB操作,连接会被无意义地持有更久,建议去掉这个手动调用。
  3. 事务传播配置的影响:如果这个DAO方法被其他带@Transactional的方法调用,默认的REQUIRED传播会让当前方法加入外层事务,连接会被外层事务持有到外层方法结束。检查外层方法是否有长时间的非DB操作(比如调用外部接口、文件IO),这会导致连接被占用很久。

三、数据库端的隐式阻塞(最容易被忽略)

服务器不繁忙但连接池被占满,很大概率是PostgreSQL里有锁等待或空闲事务:

  • 执行以下SQL排查:
    -- 查看等待中的锁
    SELECT * FROM pg_locks WHERE NOT granted;
    -- 查看长时间处于事务空闲状态的连接(这些连接未释放,占用数据库资源)
    SELECT pid, query_start, state, query FROM pg_stat_activity WHERE state = 'idle in transaction';
    
    如果查到有idle in transaction的连接,说明有事务开启后未提交/回滚,可能是代码手动开启事务但未处理,或是Spring事务管理出现异常。
  • 建议将PostgreSQL的idle_in_transaction_session_timeout参数设置为60000(1分钟),自动杀掉长时间空闲的事务连接,避免占用资源。

四、连接池与请求线程池的匹配问题

Spring MVC(比如Tomcat)默认的请求线程池maxThreads是200,而你的HikariCP连接池只有17,当突发请求时,200个线程同时抢17个连接,必然会出现超时——哪怕服务器CPU不繁忙,连接池也是瓶颈:

  • 可以适当增大maximumPoolSize到30-50(不要超过PostgreSQLmax_connections的70%,也就是105,留一部分资源给数据库管理员和其他服务)。
  • 或者调整Tomcat的maxThreads到与连接池大小匹配的数值,避免过多线程等待连接。

五、其他优化建议

  • 给HikariCP添加connectionTimeout配置,明确设置为30000(和错误日志一致),避免默认值变化:
    <beans:property name="connectionTimeout" value="30000"/>
    
  • 检查Hibernate的二级缓存配置,如果缓存命中率低,会导致大量重复查询,增加连接使用频率,甚至出现连接争抢。
  • 开启PostgreSQL的慢查询日志,查看是否有执行时间过长的SQL,慢SQL会占用连接很久,导致连接池被占满。

内容的提问来源于stack exchange,提问作者We are Borg

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 04:23:12