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和连接,但有几个细节容易出问题:
- 未处理的异常导致事务挂起:如果业务代码抛出未被捕获的异常,且没配置
@Transactional(rollbackFor = Exception.class),默认只有RuntimeException才会触发回滚。检查异常处理逻辑,确保所有异常都能触发事务回滚——未回滚的事务会一直占用连接。 - 多余的手动
flush()操作:你在saveAppDetails里手动调用了session.flush(),@Transactional方法结束时会自动提交事务并触发flush,手动flush会让连接提前进入"活跃"状态,若后续有非DB操作,连接会被无意义地持有更久,建议去掉这个手动调用。 - 事务传播配置的影响:如果这个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
相关产品推荐
相关产品推荐

