Java Spring MVC+Hibernate中Windows服务账号获取数据库连接遇死锁问题
解决同一Tomcat实例多应用下Windows服务账号无法获取数据库连接的死锁问题
问题根源分析
- 多应用并发初始化SessionFactory:每个Spring MVC应用启动时都会独立初始化Hibernate SessionFactory,触发数据库连接建立。而Windows身份验证(如Kerberos)的票据获取存在并发限制,多个应用同时发起请求会引发资源竞争,最终导致死锁。
- 连接池配置冲突:每个应用单独配置的Hibernate连接池(如c3p0、HikariCP)若初始化连接数过大,多应用叠加后会超出数据库连接上限,或因连接回收机制不合理导致连接无法释放,引发死锁。
- 类加载与上下文隔离不足:Tomcat全局类加载器共享JDBC驱动或Hibernate类实例时,多个应用会共用同一个身份验证上下文,导致线程阻塞死锁。
针对性解决方案
1. 延迟SessionFactory初始化,分散并发压力
修改Spring配置,将SessionFactory设为懒加载,避免Tomcat启动时多个应用同时初始化:
<bean id="sessionFactory" class="org.springframework.orm.hibernate5.LocalSessionFactoryBean" lazy-init="true"> <!-- 原有Hibernate配置 --> </bean>
Java配置方式:
@Bean @Lazy public LocalSessionFactoryBean sessionFactory() { LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean(); // 设置数据源、实体扫描等配置 return sessionFactory; }
这样SessionFactory会在第一个业务请求到来时才初始化,分散多个应用的资源竞争时机。
2. 改用Tomcat全局JNDI数据源,统一管理连接
放弃每个应用单独配置连接池,由Tomcat统一管理数据库连接,Windows身份验证仅在Tomcat层面处理一次:
- 在Tomcat的
conf/context.xml中配置全局数据源:
<Resource name="jdbc/MyDatabase" auth="Container" type="javax.sql.DataSource" driverClassName="com.microsoft.sqlserver.jdbc.SQLServerDriver" url="jdbc:sqlserver://localhost:1433;databaseName=MyDB;integratedSecurity=true" maxTotal="100" maxIdle="20" minIdle="5" maxWaitMillis="10000"/>
- 在每个应用的
WEB-INF/web.xml中引用该数据源:
<resource-ref> <description>数据库连接</description> <res-ref-name>jdbc/MyDatabase</res-ref-name> <res-type>javax.sql.DataSource</res-type> <res-auth>Container</res-auth> </resource-ref>
- 修改Spring配置,使用JNDI数据源:
<bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean"> <property name="jndiName" value="java:comp/env/jdbc/MyDatabase"/> </bean>
所有应用共享同一连接池,避免重复发起Windows身份验证请求,从根源降低竞争。
3. 严格限制连接池大小,避免资源耗尽
若必须保留应用独立连接池,需控制每个应用的连接数,确保总连接数不超过数据库上限。以HikariCP为例:
# HikariCP配置 hibernate.hikari.maximumPoolSize=15 hibernate.hikari.minimumIdle=3 hibernate.hikari.connectionTimeout=5000 hibernate.hikari.idleTimeout=300000 # 开启连接泄漏检测 hibernate.hikari.leakDetectionThreshold=2000
通过泄漏检测及时定位未释放的连接,避免连接耗尽引发死锁。
4. 隔离JDBC驱动类加载
将JDBC驱动包放在每个应用的WEB-INF/lib目录下,或通过Tomcat的shared.loader配置实现类加载隔离:
在conf/catalina.properties中添加:
shared.loader=${catalina.base}/lib/sqljdbc42.jar
确保每个应用使用独立的驱动实例,避免身份验证上下文冲突。
5. 排查Windows服务账号与Kerberos配置
确认Tomcat服务账号拥有数据库访问权限,可添加JVM参数开启Kerberos调试,定位身份验证阻塞点:
-Djava.security.krb5.conf=C:\Windows\krb5.ini -Dsun.security.krb5.debug=true
通过调试日志查看票据获取过程中的异常,调整Kerberos配置避免并发阻塞。
内容的提问来源于stack exchange,提问作者Romy
相关产品推荐
相关产品推荐

