Sql Server запрос из Java/ Hibernate случайно истекает при вызове в цикле
Я столкнулся со странной проблемой при попытке выполнить SQL-запрос от вызова Java к SQLServer.
Один вызов java-функции идет хорошо, но когда я делаю то же самое в цикле (от 2 до 30 итераций), я иногда (не всегда) получаю такую ошибку:
Вызывается: com.microsoft.sqlserver.jdbc.SQLServerException: Le délai imparti à la recête expire. на com.microsoft.sqlserver.jdbc.TDSCommand.checkForInterrupt(IOBuffer.java:5918)
Несколько вещей, которые я заметил:
- Он всегда терпит неудачу на одном и том же шаге цикла, при условии, что повторяющийся элемент остается неизменным (например, если он потерпит неудачу на шаге 7 из 15, он всегда потерпит неудачу на шаге 7!)
- Он потерпит неудачу на другом шаге, если я пропущу или изменим какой-либо повторяющийся элемент, включая ранее ошибочный элемент... и он не потерпит неудачу на следующем шаге, но - как это выглядит - на случайном уровне
- Если я копирую / вставляю ошибочный запрос (из отладки) в SqlQueryBrowser и выполняю его: он работает
- Если я копирую / вставляю ошибочный запрос из Sql Server Profiler: он работает!
- Если я выполню цикл из браузера запросов SQLServer (итерируя по входным параметрам): это работает!
- Если я выполню цикл из теста JUnit, только итерируя по параметрам: это тоже работает!!
- Из Sql Server Profiler я вижу, что успешные запросы занимают от 200 до 600 мс, а если сбой занимает ровно 15000 мс ( = текущее настроенное время ожидания)
- Когда я изменяю тайм-аут, запрос продолжает истекать.
Это то, что я пытался выполнить, но безуспешно:
- Используйте одну транзакцию на шаг / запрос
- Обновите сервер 'timeout соединения' и 'timeout запроса' через sp_ serveroption (EXEC sp_serveroption 'myServer \ SQLEXPRESS', 'timeout соединения', 45;)
- Изменить queryTimeout из Java
- Измените параметр "запрос ожидания" в Sql Query Browser {Сервер} > "Свойства" > "Дополнительно" > "Параллелизм"
- Не удаляйте временную таблицу #MATCHINGDAYS
- Добавьте случайный токен в #MATCHINGDAYS, чтобы предотвратить конфликты параллелизма
- Добавьте BEGIN / END вокруг запроса
- Задержка запроса с WAYITFOR 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
Некоторая другая информация
- Ява 1.7_071
- Hibernate 4.3.4.Final
- SqlServer Express 2014
- Tomcat 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 ответ
В конце концов мне удалось заставить его работать: перемещение метода "saveTableObject" из цикла в другое делает свое дело... Но, хотя я нашел решение, я все еще не понимаю, почему предыдущая вставка, встроенная в свою собственную выделенная транзакция, заставила эту блокировку поддерживать до следующей итерации и в другой транзакции!... Так что, если у вас есть советы: они все еще приветствуются!
Кстати, этот пост очень помог мне узнать, какой объект заблокирован!
@ Джин: большое спасибо за ваши многочисленные вопросы, которые помогли мне сосредоточиться на основной причине!