【问题标题】:Sql Server query from java/hibernate randomly times out when called within a loop在循环中调用时,来自 java/hibernate 的 Sql Server 查询随机超时
【发布时间】:2018-05-08 16:34:46
【问题描述】:

我在尝试从 java 调用到 sqlserver 执行 sql 查询时遇到一个奇怪的问题。

对 java 函数的一次调用运行良好,但是当我在循环中执行相同操作时(从 2 到 30 次迭代),我有时(并非总是)得到这样的错误:

原因:com.microsoft.sqlserver.jdbc.SQLServerException: Le délai Giveti à la requête a expiré。 在 com.microsoft.sqlserver.jdbc.TDSCommand.checkForInterrupt(IOBuffer.java:5918)

我注意到的几件事:

  • 它总是在循环的同一步骤失败,只要迭代项保持不变(例如,如果它在 15 步中的第 7 步失败,它总是在第 7 步失败!)
  • 如果我跳过或更改任何迭代项目(包括先前失败的项目),它将在另一个步骤失败...并且它不会在下一步失败,但是 - 看起来 - 随机一个
  • 如果我将失败的查询(来自调试)复制/粘贴到 SqlQueryBrowser 并执行它:它可以工作
  • 如果我从 Sql Server Profiler 复制/粘贴失败的查询:它可以工作!
  • 如果我从 sql server 查询浏览器中执行循环(迭代输入参数):它可以工作!
  • 如果我在 JUnit 测试中执行循环,只对参数进行迭代:它也有效!!
  • 从 Sql Server Profiler 中,我可以看到成功的请求需要 200 到 600 毫秒才能完成,而失败的请求恰好需要 15000 毫秒(= 当前配置的超时)
  • 当我更改超时时,查询一直超时。

这是我尝试执行的操作,但没有成功:

  • 每个步骤/查询使用一个事务
  • 通过 sp_serveroption 更新服务器“连接超时”和“查询超时”(EXEC sp_serveroption 'myServer\SQLEXPRESS', 'connect timeout', 45;
  • 从 java 更改 queryTimeout
  • 从 Sql Query Browser {Server} > “properties” > “advanced” > “parallelism”更改“query wait”选项
  • 不要删除临时表#MATCHINGDAYS
  • 向#MATCHINGDAYS 添加随机令牌以防止并发冲突
  • 在查询周围添加 BEGIN / END
  • WAITFOR DELAY t延迟请求
  • 使用存储过程而不是保存请求的字符串
  • 在“finally”语句中执行刷新 + 清除会话。
  • SqlServer + tomcat 重启
  • 更新驱动到 mssql-jdbc 6.1.0.jre7
  • 使用实际表而不是临时表

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 

其他一些信息

  • java 1.7_071
  • 休眠 4.3.4.Final
  • SqlServer Express 2014
  • 雄猫 7
  • sqljdbc4 4.0

编辑 28/11:原因

在我的循环中的某个地方,不时插入来自查询项的同一个表的对象。它在一个单独的事务中执行(尽管我也尝试在同一个事务中执行它)。插入对象时迭代不会出现问题,但是在接下来的迭代中......现在,我有原因......我仍然不明白为什么这会导致下一次迭代失败......虽然我怀疑一旦我理解它就会非常明显!

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
}

...这里欢迎任何提示!

【问题讨论】:

  • 很多可能的问题: 1. 查询运行缓慢,有时,取决于参数值超时。 2. 异步调用正在建立,并且在查询未完成对先前迭代的回答时调用循环迭代。 3 会话工厂工作不正常。 3. 太多的迭代 18k 迭代在那个循环中,每个 200ms 意味着 1 小时的查询运行。请记住,200 毫秒不是查询的好时机,也许您可​​以切断该循环并获取您需要的所有记录,只需更改查询即可一次性完成
  • 让,感谢您的评论。 1 因为这与特定的输入参数无关,并且由于查询本身(循环外)工作正常,所以这听起来不像 pb. 2 我同意,但是这里的调用是同步的。 3 SessionFactory 已经工作了 5 年(不时更新)..我不明白这里可能有什么问题......我认为任何 pb 都应该被 nom 检测到(你在这里有提示吗? ) 4 我忘了在这里说明迭代次数:它从 2 到 30.... 远非 18k!
  • 相对于 1,当循环调用在每次迭代中传递客户 ID 以获取他的订单的查询时,它会记住一次。在特定客户 ID 上导致超时。 2.尝试更改连接超时,看看是否有助于找出罪魁祸首。如果只有几次迭代,我可以设置一个断点并测试每个查询调用
  • 是的,我确实尝试了这两种方法:更改查询超时(无论超时是多少)并设置一个断点,以便测试失败的查询或挖掘 Session 对象:没有成功!
  • 这里有异味。如果您可以使用相同的连接/用户在相同的服务器/基础上运行它,那么问题不是查询本身,而是超时显然意味着对数据库的调用需要很长时间才能完成。也许它是某种锁或您的测试中的某些东西也不正确。我只能尝试一个远射,使用表变量而不是临时表来确保调用之间没有问题

标签: java sql-server hibernate tsql sqljdbc


【解决方案1】:

最终,我设法让它工作:将“saveTableObject”方法从循环内移到循环外就可以了... 但是,尽管我找到了解决方案,但我仍然不明白为什么嵌入在其自己的专用事务中的先前插入会导致此锁保持到下一次迭代和另一个事务中!...所以,如果您有提示:它们是仍然欢迎!

顺便说一句,这个post帮我找出了哪个对象被锁定了!

@Jean:非常感谢您提出的许多问题,这些问题帮助我专注于核心事业!

【讨论】:

    猜你喜欢
    • 2021-02-10
    • 1970-01-01
    • 2017-04-24
    • 1970-01-01
    • 1970-01-01
    • 2014-06-01
    • 2019-02-26
    • 1970-01-01
    相关资源
    最近更新 更多