Sql 来自 java/hibernate 的服务器查询在循环中调用时随机超时
Sql Server query from java/hibernate randomly times out when called within a loop
我在尝试从对 sql 服务器的 java 调用执行 sql 查询时遇到了一个奇怪的问题。
对 java 函数的一次调用运行良好,但是当我在一个循环中(从 2 到 30 次迭代)执行相同的操作时,我有时(并非总是)遇到这样的错误:
Caused by: com.microsoft.sqlserver.jdbc.SQLServerException: Le délai imparti à la requête a expiré.
at com.microsoft.sqlserver.jdbc.TDSCommand.checkForInterrupt(IOBuffer.java:5918)
我注意到的几件事:
- 它总是在循环的同一步骤失败,前提是迭代项保持不变(例如,如果它在第 7 步失败,共 15 步,它总是会在第 7 步失败!)
- 如果我跳过或更改任何重复的项目,包括之前失败的项目,它会在另一步失败......并且它不会在下一步失败,但是 - 看起来像 - 随机的
- 如果我 copy/paste 将失败的查询(来自调试)输入 SqlQueryBrowser 并执行它:它有效
- 如果我 copy/paste 来自 Sql Server Profiler 的失败查询:它有效!
- 如果我从 sql 服务器查询浏览器中执行循环(遍历输入参数):它有效!
- 如果我从 JUnit 测试中执行循环,只迭代参数:它也有效!!
- 从 Sql Server Profiler,我可以看到成功的请求需要 200 到 600 毫秒才能完成,而失败的请求恰好需要 15000 毫秒(= 当前配置的超时)
- 当我更改超时时,查询一直超时。
这是我尝试执行的操作,但没有成功:
- 每个步骤/查询使用一个事务
- 通过 sp_serveroption 更新服务器 'connect timeout' et 'query timeout' (EXEC sp_serveroption 'myServer\SQLEXPRESS', 'connect timeout', 45 ;)
- 从 java
更改 queryTimeout
- 更改 "query wait" 选项 Sql 查询浏览器 {Server} > "properties" > "advanced" > "parallelism"
- 不要删除临时 table #MATCHINGDAYS
- 向#MATCHINGDAYS 添加一个随机标记以防止并发冲突
- 在查询周围添加 BEGIN / END
- 延迟请求 WAITFOR DELAY t
- 使用存储过程而不是保存请求的字符串
- 从 "finally" 语句中执行刷新 + 清除会话。
- 重新启动 Sql服务器 + tomcat
- 更新驱动到mssql-jdbc 6.1.0.jre7
- 使用实际的 table 而不是临时的
java调用
(它嵌入在单个事务中)
final SessionImpl sess = (SessionImpl) this.getSessionFactory().openSession();
try (Connection conn = sess.connection(); Statement st = conn.createStatement();) {
final NamedParameterPreparedStatement ps = NamedParameterPreparedStatement.createNamedParameterPreparedStatement(conn, aStringWithTheRequestBelow);
// ... set parameters here ...
ps.setQueryTimeout(15);
final ResultSet rs = ps.executeQuery();
} catch (final SQLException ex) {
LOG.error("parameter 1 :" + parameter1 + "etc.");
} finally {
sess.close();
}
TSQL 查询
(我把它做到了脑和眼友好,但它看起来有点棘手;但基本上,你已经有了它的想法)
--DECLARE @date as DATE = '2017-09-03'; -- one of the input parameter set up from java.
IF OBJECT_ID('tempdb.dbo.#MATCHINGDAYS718567154','U') is not null
DROP TABLE #MATCHINGDAYS718567154; -- RANDOM TOKEN HERE TO AVOID CONCURRENCY
WITH startDateTable AS (
SELECT TOP 1 a.dateStart
FROM someSelection
),
endDateTable AS (
SELECT TOP 1 endDate
FROM anotherSelection
),
AllDays AS (
SELECT myFunc_getMaxDate(DATEADD(DAY,1,startDateTable.dateStart), DATEADD(dd, -30,@date))AS [Date]
FROM startDateTable
UNION ALL
SELECT
DATEADD(DAY, 1, [Date])
from AllDays
join endDateTable on 1=1
WHERE [Date] < myFunc_getMinDate(DATEADD(dd, +7, @date),endDateTable.endDate))
-- build a temporary table with all days from startDate to endDate the recursive way
-- with a min -30 days before input date and 7 days after output date
SELECT [Date]
INTO #MATCHINGDAYS718567154
FROM AllDays
OPTION (MAXRECURSION 37)
SELECT
manyFields
from MainTable
-- various joins
join #MATCHINGDAYS718567154 as MD on 1 = 1
where 1 = 1
and -- etc... many clauses including some computed over MATCHINGDAYS718567154
order by someField
DROP TABLE #MATCHINGDAYS718567154
一些其他信息
- java1.7_071
- 休眠 4.3.4.Final
- Sql服务器 Express 2014
- Tomcat 7
- sqljdbc4 4.0
编辑 28/11:原因
在我的循环中的某个地方,不时插入来自查询项目的相同 table 的对象。它在单独的事务中执行(尽管我也尝试在同一事务中执行)。插入对象时的迭代不会出现问题,但在接下来的迭代中......现在,我有原因......我仍然不明白为什么这会导致下一次迭代失败......虽然我怀疑一旦我理解它就会非常明显!
someIterationOver(List<SomeObject> manyObjects){
from(SomeObject mo : manyObjects){
List<MyTableObject> myTableObjects = performMyBigQueryOver(mo.getSomeId());
for(MyTableObject myTableSingleObject : myTableObjects){
if(myTableSingleObject.matchesSomeCondition()){
TableSingleObject aNewTableSingleObject = new TableSingleObject("newTableObject");
// this will cause the problem in the next iteration
saveTableObject(aNewTableSingleObject);
}
}
}
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
private void saveTableObject(ableSingleObject aNewTableSingleObject){
this.sessionFactory.getCurrentSession.save(aNewTableSingleObject); // works fine
}
...欢迎任何提示!
最终,我设法让它工作:将 "saveTableObject" 方法从循环内移到循环外就成功了......
但是,尽管我找到了解决方案,但我仍然不明白为什么嵌入在其自己专用事务中的先前插入会导致此锁保持到下一次迭代和另一个事务中!...因此,如果您有提示:它们是仍然欢迎!
顺便说一下,这个 post 对我找出锁定了哪个对象帮助很大!
@Jean:非常感谢您提出的许多问题,帮助我专注于核心原因!
我在尝试从对 sql 服务器的 java 调用执行 sql 查询时遇到了一个奇怪的问题。
对 java 函数的一次调用运行良好,但是当我在一个循环中(从 2 到 30 次迭代)执行相同的操作时,我有时(并非总是)遇到这样的错误:
Caused by: com.microsoft.sqlserver.jdbc.SQLServerException: Le délai imparti à la requête a expiré. at com.microsoft.sqlserver.jdbc.TDSCommand.checkForInterrupt(IOBuffer.java:5918)
我注意到的几件事:
- 它总是在循环的同一步骤失败,前提是迭代项保持不变(例如,如果它在第 7 步失败,共 15 步,它总是会在第 7 步失败!)
- 如果我跳过或更改任何重复的项目,包括之前失败的项目,它会在另一步失败......并且它不会在下一步失败,但是 - 看起来像 - 随机的
- 如果我 copy/paste 将失败的查询(来自调试)输入 SqlQueryBrowser 并执行它:它有效
- 如果我 copy/paste 来自 Sql Server Profiler 的失败查询:它有效!
- 如果我从 sql 服务器查询浏览器中执行循环(遍历输入参数):它有效!
- 如果我从 JUnit 测试中执行循环,只迭代参数:它也有效!!
- 从 Sql Server Profiler,我可以看到成功的请求需要 200 到 600 毫秒才能完成,而失败的请求恰好需要 15000 毫秒(= 当前配置的超时)
- 当我更改超时时,查询一直超时。
这是我尝试执行的操作,但没有成功:
- 每个步骤/查询使用一个事务
- 通过 sp_serveroption 更新服务器 'connect timeout' et 'query timeout' (EXEC sp_serveroption 'myServer\SQLEXPRESS', 'connect timeout', 45 ;)
- 从 java 更改 queryTimeout
- 更改 "query wait" 选项 Sql 查询浏览器 {Server} > "properties" > "advanced" > "parallelism"
- 不要删除临时 table #MATCHINGDAYS
- 向#MATCHINGDAYS 添加一个随机标记以防止并发冲突
- 在查询周围添加 BEGIN / END
- 延迟请求 WAITFOR DELAY t
- 使用存储过程而不是保存请求的字符串
- 从 "finally" 语句中执行刷新 + 清除会话。
- 重新启动 Sql服务器 + tomcat
- 更新驱动到mssql-jdbc 6.1.0.jre7
- 使用实际的 table 而不是临时的
java调用
(它嵌入在单个事务中)
final SessionImpl sess = (SessionImpl) this.getSessionFactory().openSession();
try (Connection conn = sess.connection(); Statement st = conn.createStatement();) {
final NamedParameterPreparedStatement ps = NamedParameterPreparedStatement.createNamedParameterPreparedStatement(conn, aStringWithTheRequestBelow);
// ... set parameters here ...
ps.setQueryTimeout(15);
final ResultSet rs = ps.executeQuery();
} catch (final SQLException ex) {
LOG.error("parameter 1 :" + parameter1 + "etc.");
} finally {
sess.close();
}
TSQL 查询
(我把它做到了脑和眼友好,但它看起来有点棘手;但基本上,你已经有了它的想法)
--DECLARE @date as DATE = '2017-09-03'; -- one of the input parameter set up from java.
IF OBJECT_ID('tempdb.dbo.#MATCHINGDAYS718567154','U') is not null
DROP TABLE #MATCHINGDAYS718567154; -- RANDOM TOKEN HERE TO AVOID CONCURRENCY
WITH startDateTable AS (
SELECT TOP 1 a.dateStart
FROM someSelection
),
endDateTable AS (
SELECT TOP 1 endDate
FROM anotherSelection
),
AllDays AS (
SELECT myFunc_getMaxDate(DATEADD(DAY,1,startDateTable.dateStart), DATEADD(dd, -30,@date))AS [Date]
FROM startDateTable
UNION ALL
SELECT
DATEADD(DAY, 1, [Date])
from AllDays
join endDateTable on 1=1
WHERE [Date] < myFunc_getMinDate(DATEADD(dd, +7, @date),endDateTable.endDate))
-- build a temporary table with all days from startDate to endDate the recursive way
-- with a min -30 days before input date and 7 days after output date
SELECT [Date]
INTO #MATCHINGDAYS718567154
FROM AllDays
OPTION (MAXRECURSION 37)
SELECT
manyFields
from MainTable
-- various joins
join #MATCHINGDAYS718567154 as MD on 1 = 1
where 1 = 1
and -- etc... many clauses including some computed over MATCHINGDAYS718567154
order by someField
DROP TABLE #MATCHINGDAYS718567154
一些其他信息
- java1.7_071
- 休眠 4.3.4.Final
- Sql服务器 Express 2014
- Tomcat 7
- sqljdbc4 4.0
编辑 28/11:原因
在我的循环中的某个地方,不时插入来自查询项目的相同 table 的对象。它在单独的事务中执行(尽管我也尝试在同一事务中执行)。插入对象时的迭代不会出现问题,但在接下来的迭代中......现在,我有原因......我仍然不明白为什么这会导致下一次迭代失败......虽然我怀疑一旦我理解它就会非常明显!
someIterationOver(List<SomeObject> manyObjects){
from(SomeObject mo : manyObjects){
List<MyTableObject> myTableObjects = performMyBigQueryOver(mo.getSomeId());
for(MyTableObject myTableSingleObject : myTableObjects){
if(myTableSingleObject.matchesSomeCondition()){
TableSingleObject aNewTableSingleObject = new TableSingleObject("newTableObject");
// this will cause the problem in the next iteration
saveTableObject(aNewTableSingleObject);
}
}
}
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
private void saveTableObject(ableSingleObject aNewTableSingleObject){
this.sessionFactory.getCurrentSession.save(aNewTableSingleObject); // works fine
}
...欢迎任何提示!
最终,我设法让它工作:将 "saveTableObject" 方法从循环内移到循环外就成功了...... 但是,尽管我找到了解决方案,但我仍然不明白为什么嵌入在其自己专用事务中的先前插入会导致此锁保持到下一次迭代和另一个事务中!...因此,如果您有提示:它们是仍然欢迎!
顺便说一下,这个 post 对我找出锁定了哪个对象帮助很大!
@Jean:非常感谢您提出的许多问题,帮助我专注于核心原因!