com.microsoft.sqlserver.jdbc.sqlserverexception:流已关闭问题原因排查咨询
问题分析:com.microsoft.sqlserver.jdbc.SQLServerException: The stream is closed
环境信息
- SQL Server版本:
Microsoft SQL Server 2012 - 11.0.5548.0 (X64) Sep 6 2014 17:19:28 Copyright (c) Microsoft Corporation Enterprise Edition (64-bit) on Windows NT 6.3 <X64> (Build 9600: ) (Hypervisor) - Hibernate版本:
hibernate-core-5.6.11.Final - JRE版本:
jre1.8.0_281 - Apache Tomcat版本:
9.0.71 - Spring Boot版本:
2.7.4 - JDBC驱动:
mssql-jdbc-12.2.0.jre8.jar - 连接池:
HikariCP-4.0.3.jar
Spring数据源配置
spring.datasource.url=jdbc:sqlserver://mydatabaseServer:51803;databaseName=mydatabaseName;encrypt=false;trustServerCertificate=true;socketTimeout=60000 spring.datasource.driver-class-name=com.microsoft.sqlserver.jdbc.SQLServerDriver spring.datasource.password = mypassword spring.datasource.testWhileIdle = true spring.datasource.test-on-borrow=true spring.datasource.validationQuery = SELECT 1 spring.datasource.sql-script-encoding=UTF-8 spring.datasource.username = myusername spring.datasource.hikari.pool-name=myPool spring.datasource.hikari.maximum-pool-size=300 spring.datasource.hikari.connectionTimeout=80000 spring.datasource.hikari.max-lifetime=300000 spring.datasource.hikari.idle-timeout=60000 spring.datasource.hikari.leakDetectionThreshold=300000 spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.SQLServer2012Dialect spring.jpa.properties.hibernate.enable_lazy_load_no_trans=true spring.jpa.open-in-view=false spring.jpa.hibernate.naming-strategy = org.hibernate.cfg.ImprovedNamingStrategy
问题查询代码
@Repository @Transactional(readOnly = true) @Log4j2 public class SearchService { @PersistenceContext private EntityManager entityManager; public List<Request> searchRequests(SearchModel searchModel, int pageNumber) { CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder(); CriteriaQuery<Request> criteriaQuery = criteriaBuilder.createQuery(Request.class); Root<Request> requestRoot = criteriaQuery.from(Request.class); requestRoot.alias("r"); Predicate predicate = createConditions(requestRoot, criteriaBuilder, searchModel); criteriaQuery.select(requestRoot); Order orderBy = criteriaBuilder.desc(requestRoot.get("id")); String orderByProperty = searchModel.getOrderBy(); criteriaQuery.where(predicate).orderBy(orderBy); // pageSize is 10 // searchModel.getMaxResult() = 10 // for pageNumber 1 startFrom will be 0 and maxResult will be 10 int startFrom = (pageNumber - 1) * searchModel.getPageSize(); List<Request> result = entityManager.createQuery(criteriaQuery).setMaxResults(searchModel.getMaxResult()) .setFirstResult(startFrom).getResultList(); return result; } }
异常情况描述
该配置长期运行正常,但近期频繁抛出com.microsoft.sqlserver.jdbc.SQLServerException: The stream is closed异常,异常触发点为查询结果获取代码:
entityManager.createQuery(criteriaQuery).setMaxResults(searchModel.getMaxResult()) .setFirstResult(startFrom).getResultList();
对应的SQL语句为:
SELECT * FROM request ORDER BY id OFFSET 0 ROWS FETCH NEXT 10 ROWS ONLY;
直接在SQL Server执行该查询仅需10ms,但监控显示异常多次发生在该查询中。注意:Request实体/表无Eager关联和二进制列,但包含两个varchar(MAX)列,最大长度为5368。
可能的原因分析
应用端原因
- 连接池配置不匹配:HikariCP的
max-lifetime设置为300秒,若SQL Server端的连接超时(如remote query timeout或连接生命周期)小于该值,会导致数据库主动关闭连接,但连接池仍认为该连接可用,当应用复用该连接时就会触发流关闭异常。另外testWhileIdle和test-on-borrow同时开启,可能存在校验逻辑冲突。 - JDBC驱动兼容性问题:mssql-jdbc-12.2.0是较新版本,对SQL Server 2012这种旧版本数据库可能存在兼容性bug,尤其是在处理varchar(MAX)列的结果流时,可能出现流被意外关闭的情况。
- Hibernate结果集处理问题:虽然查询本身执行快,但Hibernate在映射varchar(MAX)列到实体时,可能因连接提前释放导致流中断。比如
@Transactional(readOnly=true)的事务边界是否覆盖了结果集读取的全流程,或者EntityManager的生命周期管理存在异常。 - 连接泄漏风险:虽然配置了
leakDetectionThreshold=300000,但如果存在请求处理时间超过5分钟的情况,连接池会标记连接泄漏并回收,此时若应用仍在使用该连接读取结果,就会触发异常。
数据库/服务器端原因
- SQL Server连接超时设置变更:近期可能修改了数据库的
remote query timeout、connection timeout等参数,导致数据库主动关闭长时间闲置或处理中的连接。 - 网络不稳定:应用服务器与数据库服务器之间的网络出现丢包、延迟波动或临时中断,即使查询执行完成,结果传输过程中连接断开也会触发流关闭异常。
- SQL Server资源瓶颈:数据库服务器内存不足、CPU使用率过高,导致查询结果的缓存或传输出现异常,强制关闭连接;或者数据库线程池耗尽,无法处理新的请求导致连接中断。
- Windows服务器TCP配置问题:Windows服务器的TCP连接超时、端口耗尽等系统级配置问题,导致操作系统主动关闭数据库连接。
排查建议
- 查看SQL Server错误日志,确认是否有连接关闭相关的异常记录。
- 调整HikariCP的
max-lifetime,设置为比数据库端连接超时小30秒左右(例如若数据库连接超时为360秒,将max-lifetime设为330秒),避免数据库主动关闭连接池中的可用连接。 - 尝试降级JDBC驱动版本至对SQL Server 2012支持更稳定的版本(如mssql-jdbc-9.4.1.jre8),验证是否为驱动兼容性问题。
- 监控应用与数据库之间的网络状态,检查是否存在丢包、延迟异常等情况。
- 开启JDBC驱动的debug日志,查看流关闭的具体时机和触发条件,定位问题根源。
内容的提问来源于stack exchange,提问作者Mahmoud Saleh
相关产品推荐
相关产品推荐

