我正在处理的大型单体 Spring Boot 应用程序存在“数据库连接”问题,所有这些都归结为应用程序未将数据库连接释放回池中的事实。
考虑以下场景:从控制器调用的服务方法:
void myMethod() {
userRepository.findById(userId);
thread.sleep(4000); // simulate remote method call
userFriendsRepository.getFriendsByUserId(userId);
thread.sleep(4000); // simulate remote method call
}
应用程序.properties
# Throw a warning when connection is not released
spring.datasource.hikari.leak-detection-threshold=3000
我们正在尝试评估潜在的解决方案:
经过研究,这个问题有一个明显的解决方案是在视图模式中禁用打开会话(因为 Spring 在整个 http 请求期间保持休眠会话):
spring.jpa.open-in-view = false
在这种情况下,副作用与实体关系有关。
禁用自动提交并将休眠连接设置设置为在语句后释放:
spring.datasource.hikari.auto-commit=false
spring.jpa.properties.hibernate.connection.handling_mode=
DELAYED_ACQUISITION_AND_RELEASE_AFTER_STATEMENT
这种方法不太清楚。
我的主要问题:
为什么使用方法 #2 警告
Apparent connection leak detected
仍然显示,即使属性说“声明后释放连接”。我的理解是,在此上下文中的“语句”是一个 SQL 查询,应该在方法 userRepository.findById(userId);
之后和 thread.sleep(4000);
甚至开始之前立即发布。请注意,这里我只考虑#2,“在视图中打开”仍然启用。
有趣的观察:使用#2 并将
thread.sleep
减小到2000
,Apparent connection leak detected
不再显示。即使两个超时加起来超过“泄漏阈值”。似乎连接是在执行下一个查询之前释放的,而不是在完成上一个查询之后立即释放的(即使打开了 open-session-in-view )。
在#2 中,禁用自动提交会影响应用程序吗?我看到 Spring 已经在每个事务中禁用了自动提交:
public class DataSourceTransactionManager {
protected void doBegin(...) {
...
con.setAutoCommit(false);
...
}
}
According to this comment,它不应该,但我想知道为什么它在 Spring 默认情况下打开。是因为向后兼容吗?
您在项目中遇到过类似的数据库连接问题吗?您是如何解决的?
我个人倾向于第 1 种方法,即禁用 open-session-in-view,但我想在做出决定之前了解这两种方法,以及它们将对现有应用程序产生哪些影响。
附言Vlad Mihalcea 有一篇关于这个主题的优秀文章,但它没有比较这两种方法并回答我的具体问题(或者我可能没有 100% 理解这篇文章)。