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

Hibernate save调用引发线程挂起问题排查求助

问题分析:Hibernate save方法触发线程挂起

先说说你的场景:咱们系统用的是Hibernate 4.3.10 + Spring的DAO模式,每个数据库操作对应独立DAO类,数据库是SQL Server。最近碰到个棘手的情况——调用Hibernate的save()方法时线程直接挂起,但同款代码在另一个同版本的独立服务器+独立数据库环境下跑完全没问题。更奇怪的是,应用代码已经两年没改过了,这就有点让人摸不着头脑。

先贴一下你提供的关键代码和配置:

核心DAO代码

// BusinessAlerts是普通POJO类
@Override
@Transactional(propagation = Propagation.REQUIRED)
public void persistBusinessAlert(BusinessAlerts businessAlert) throws DAOException {
    if (businessAlert == null) {
        return;
    }
    try {
        currentSession().save(businessAlert);
    } catch (HibernateException e) {
        throw new DAOException(e.getMessage(), e);
    }
}

Hibernate相关配置(Spring XML)

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
<tx:annotation-driven transaction-manager="priceTransactionManager" />
<bean class="mro.util.CryptoPropertyPlaceholderConfigurer">
<property name="location" value="common_db.properties" />
</bean>
<bean id="priceDataSource" destroy-method="close" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="${db.driver}" />
<property name="url" value="${alert.db.url}" />
<property name="username" value="${alert.db.username}" />
<property name="password" value="${alert.db.password}" />
</bean>
<bean id="priceSessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource" ref="priceDataSource" />
<property name="annotatedClasses">
<list>
<value>BusinessAlerts</value>
<value>Executions</value>
<value>AlertCodes</value>
</list>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.SQLServerDialect</prop>
<prop key="hibernate.show_sql">true</prop>
</props>
</property>
</bean>
<bean id="priceTransactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="priceSessionFactory" />
</bean>
</beans>

currentSession()的调用逻辑

protected final org.hibernate.Session currentSession() throws org.springframework.dao.DataAccessResourceFailureException;
0 aload_0 [this]
1 invokevirtual org.springframework.orm.hibernate4.support.HibernateDaoSupport.getSessionFactory() : org.hibernate.SessionFactory [10]
4 invokeinterface org.hibernate.SessionFactory.getCurrentSession() : org.hibernate.Session [11] [nargs: 1]
9 areturn
Line numbers: [pc: 0, line: 129]
Local variable table: [pc: 0, pc: 10] local: this index: 0 type: org.springframework.orm.hibernate4.support.HibernateDaoSupport

可能的排查方向

结合你的场景,我整理了几个优先级较高的排查点:

1. 数据库连接池耗尽(DBCP配置缺失)

你用的是org.apache.commons.dbcp.BasicDataSource,但目前的配置里完全没设置连接池的核心参数——比如最大连接数、获取连接的超时时间。如果当前环境的并发量比正常环境高,或者存在未释放的连接(比如事务异常导致连接没回收),很容易把连接池占满,后续请求在获取连接时就会无限挂起。

  • 建议:
    • 给DBCP补充必要的配置参数,比如:
      <property name="maxActive" value="20"/> <!-- 最大活跃连接数 -->
      <property name="maxWait" value="3000"/> <!-- 获取连接的超时时间(毫秒),超时会抛异常而不是挂起 -->
      <property name="minIdle" value="5"/> <!-- 最小空闲连接数 -->
      <property name="removeAbandoned" value="true"/> <!-- 自动回收废弃连接 -->
      <property name="removeAbandonedTimeout" value="180"/> <!-- 废弃连接的超时时间(秒) -->
      
    • 查看SQL Server的sys.dm_exec_connections视图,统计当前的数据库连接数,确认是否接近或达到上限。

2. 数据库层面的阻塞或死锁

代码没改,但数据库环境可能存在锁竞争或者死锁。当Hibernate执行save()对应的INSERT语句时,如果这条语句需要等待某个锁(比如其他事务长时间占用了BusinessAlerts表的锁,或者有未提交的事务持有相关行锁),线程就会卡在等待锁的状态。

  • 建议:
    • 在SQL Server里执行sp_who2命令,查看是否有阻塞会话,找到阻塞源(BlkBy列不为空的行),检查对应的事务是否长时间运行。
    • 查看SQL Server的死锁日志(可以通过SQL Server Management Studio的“管理->扩展事件”或者系统视图sys.dm_tran_deadlocks),确认是否有死锁发生。

3. Hibernate Session/事务的上下文绑定异常

你的currentSession()是通过SessionFactory.getCurrentSession()获取的,这个方法依赖于Spring事务管理器绑定的线程上下文Session。如果当前环境的线程上下文存在异常(比如类加载器不一致,或者事务传播行为的隐性冲突),可能导致Session无法正常获取或释放,进而卡住线程。

  • 建议:
    • 查看线程堆栈的阻塞点,如果是卡在getCurrentSession()或者DataSource.getConnection()这一步,那大概率是Session/连接的绑定问题。
    • 临时做个测试:把currentSession()换成sessionFactory.openSession(),用完手动关闭(session.close()),看是否还会挂起。如果问题消失,说明是getCurrentSession()的上下文绑定出了问题。

4. 环境隐性差异(JDBC驱动/数据库补丁)

你说其他环境版本一致,但可能存在细微的隐性差异:

  • 比如当前环境的SQL Server JDBC驱动版本和正常环境不一致,或者数据库的补丁级别不同,导致Hibernate的底层JDBC调用出现阻塞。
  • 建议:
    • 对比两个环境的JDBC驱动jar包(比如sqljdbc4.jar之类的),确保版本完全一致。
    • 检查SQL Server的版本和补丁,确认是否有已知的JDBC兼容性问题(比如某些补丁会导致INSERT操作的锁行为变化)。

5. 服务器资源瓶颈

当前环境的服务器资源(CPU、内存、磁盘IO)不足,也可能导致线程挂起:

  • 比如内存不足导致Hibernate的一级/二级缓存无法正常工作,或者数据库服务器的磁盘IO过高,导致写入操作缓慢,进而阻塞线程。
  • 建议:
    • 检查应用服务器的CPU、内存使用率,是否有资源耗尽的情况(比如内存溢出前的频繁GC,或者CPU被其他进程占满)。
    • 检查数据库服务器的磁盘读写延迟、队列长度,确认是否有IO瓶颈。

内容的提问来源于stack exchange,提问作者Vikash Jain

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.13 08:41:43