SQL Server запрос работает медленно из Java - PullRequest
9 голосов
/ 07 июня 2009

У меня есть Java-программа, которая запускает несколько запросов к базе данных сервера SQL. Первый из них, который запрашивает представление, возвращает около 750 тыс. Записей. Я могу выполнить запрос через SQL Server Studio Studio, и я получаю результаты примерно через 30 секунд. Тем не менее, я запустил программу для запуска прошлой ночью. когда я проверил его этим утром, этот запрос все еще не дал результатов обратно в java-программу, примерно через 15 часов.

У меня есть доступ к базе данных, чтобы делать все, что я хочу, но я действительно не знаю, как начать отлаживать это. Что нужно сделать, чтобы понять, что является причиной такой ситуации? Я не являюсь администратором базы данных и не очень хорошо знаком с набором инструментов сервера SQL, поэтому более подробная информация, которую вы можете дать мне о том, как сделать то, что вы предлагаете, будет оценена.

вот код

stmt = connection.createStatement();
clientFeedRS = stmt.executeQuery(StringBuffer.toString());

EDIT1:

Ну, это было давно, и это отвлеклось, но эта проблема вернулась. Я посмотрел на обновление с jdbc driver v 1.2 до 2.0, но мы застряли на jdk 1.4, а v 2.0 требует jdk 1.5, так что это не стартер. Теперь я смотрю на свойства моей строки подключения. Я вижу 2, которые могут быть полезны.

SelectMethod=cursor|direct
responseBuffering=adaptive|full

В настоящее время с проблемой задержки я работаю с курсором в качестве selectMethod и со значением по умолчанию для responseBuffering, которое заполнено. Может ли изменение этих свойств помочь? если это так, какие будут идеальные настройки? Я думаю, основываясь на том, что я могу найти в Интернете, что использование метода прямого выбора и адаптивной буферизации ответов может решить мою проблему. есть мысли?

EDIT2:

Мы закончили изменять оба этих параметра строки соединения, используя метод выбора по умолчанию (direct) и указав responseBuffering как адаптивный. Это в конечном итоге работает лучше всего для меня и устраняет проблемы с задержкой, которые я видел. спасибо за помощь.

Ответы [ 13 ]

22 голосов
/ 30 сентября 2015

У меня была похожая проблема с очень простым запросом (SELECT. FROM. WHERE =.), Который занимал до 10 секунд, чтобы вернуть одну строку при использовании соединения jdbc в Java, в то время как в sqlshell требовалось всего 0,01 секунды. Проблема была той же, независимо от того, использовал ли я официальный драйвер MS SQL или драйвер JTDS.

Решением было установить это свойство в URL-адресе jdbc: sendStringParametersAsUnicode = ложь

Полный пример, если вы используете официальный драйвер MS SQL: jdbc: sqlserver: // yourserver; instanceName = yourInstance; databaseName = yourDBName; sendStringParametersAsUnicode = false;

Инструкции по использованию различных драйверов jdbc и более подробная информация о проблеме здесь: http://emransharif.blogspot.fr/2011/07/performance-issues-with-jdbc-drivers.html

SQL Server отличает свои типы данных, поддерживающие Unicode, от тех, которые просто поддерживают ASCII. Например, символьными типами данных, поддерживающими Unicode, являются nchar, nvarchar, longnvarchar, где в качестве счетчиков ASCII используются char, varchar и longvarchar соответственно. По умолчанию все драйверы JDBC от Microsoft отправляют строки в формате Unicode на SQL Server, независимо от того, поддерживает ли тип данных соответствующего столбца, определенного в SQL Server, Unicode или нет. В случае, когда типы данных столбцов поддерживают Unicode, все гладко. Но в тех случаях, когда типы данных столбцов не поддерживают Unicode, возникают серьезные проблемы с производительностью, особенно во время выборки данных. SQL Server пытается преобразовать типы данных, отличные от Юникода в таблице, в типы данных Юникод перед выполнением сравнения. Более того, если для столбца не в Юникоде существует индекс, он будет проигнорирован. В конечном итоге это приведет к сканированию всей таблицы во время выборки данных, что существенно замедлит поисковые запросы.

В моем случае у меня было 30 миллионов записей в таблице, из которой я искал. Продолжительность выполнения запроса увеличилась с более чем 10 секунд до приблизительно 0,01 с после применения свойства.

Надеюсь, это кому-нибудь поможет!

7 голосов
/ 22 октября 2014

Возможно, это не относится к вашей конкретной ситуации, но я хотел дать другое возможное объяснение тому, кто ищет эту проблему.

У меня просто была похожая проблема, когда запрос, выполняемый непосредственно в SQL Server, занимал 1 минуту, в то время как тот же запрос занимал 5 минут через подготовленный компонент состояния Java. Я проследил это до того, что это было сделано как подготовленное заявление.

Когда вы выполняете запрос непосредственно в SQL Server, вы предоставляете ему непараметрический запрос, в котором он знает все критерии поиска во время оптимизации. В моем случае мои критерии поиска включали диапазон дат, и SQL-сервер смог посмотреть на него, решить, что «диапазон дат огромен, давайте не будем использовать индекс дат», и тогда он выбрал что-то намного лучшее.

Когда я выполняю тот же запрос через подготовленный оператор Java, в то время, когда SQL Server оптимизирует запрос, вы еще не предоставили ему ни одного из значений параметра, поэтому он должен угадать, какой индекс использовать , В случае моего диапазона дат, если он оптимизируется для небольшого диапазона, и я задаю ему большой диапазон, он будет работать медленнее, чем мог бы. Точно так же, если он оптимизируется для большого диапазона, а я даю ему маленький, он снова будет работать медленнее, чем мог бы.

Чтобы продемонстрировать, что это действительно было проблемой, в качестве эксперимента я попытался дать ему подсказки о том, что оптимизировать для использования опции «OPTIMIZE FOR» в SQL Server. Когда я сказал ему использовать крошечный диапазон дат, мой Java-запрос (который на самом деле имел широкий диапазон дат) фактически занимал в два раза больше времени, чем раньше (10 минут, по сравнению с 5 минутами раньше, и 1 минутой в SQL Server) ). Когда я сообщил точные даты для оптимизации, время выполнения было одинаковым для подготовленного Java-оператора.

Таким образом, я решил жестко запрограммировать точные даты в запросе. Это сработало для меня, потому что это было одноразовое заявление. PreparedStatement не предназначался для повторного использования, а просто для параметризации значений, чтобы избежать внедрения SQL. Поскольку эти даты были получены из объекта java.sql.Date, мне не пришлось беспокоиться о значениях дат, содержащих код внедрения.

Тем не менее, для заявления, которое ДОЛЖНО быть повторно использовано, жесткое кодирование дат не будет работать. Возможно, лучшим вариантом для этого было бы создание нескольких подготовленных выписок, оптимизированных для разных диапазонов дат (один на день, один на неделю, один на месяц, один на год и один на десятилетие ... или, может быть, вы нужны только 2 или 3 варианта ... я не знаю) и затем для каждого запроса выполните один подготовленный оператор, чей временной диапазон лучше всего соответствует диапазону в реальном запросе.

Конечно, это работает только в том случае, если ваши диапазоны дат распределены равномерно. Если 80% ваших записей были в прошлом году, а 20% - разбросаны за предыдущие 10 лет, то выполнение «нескольких запросов на основе размера диапазона» может оказаться не лучшим решением. Вы должны оптимизировать свои запросы на основе определенных диапазонов или чего-то еще. Вы должны выяснить это через испытание.

4 голосов
/ 07 июня 2009

Убедитесь, что ваш драйвер JDBC сконфигурирован для использования прямого соединения, а не соединения cusror. Вы можете опубликовать свой URL-адрес подключения JDBC, если не уверены.

Убедитесь, что вы используете набор результатов только для пересылки и только для чтения (это значение по умолчанию, если вы его не устанавливаете).

И убедитесь, что вы используете обновленные драйверы JDBC.

Если все это не работает, то вам следует взглянуть на SQL Server Profiler и попытаться перехватить SQL-запрос, поскольку драйвер jdbc выполняет оператор, и запустить этот оператор в студии управления и посмотреть, есть ли разница.

Кроме того, поскольку вы извлекаете так много данных, вам следует убедиться, что у вас нет каких-либо замедлений памяти / сбора мусора в JVM (хотя в этом случае это не объясняет расхождения во времени).

3 голосов
/ 04 января 2011

Если запрос параметризован, это может быть отсутствующий параметр или параметр, который установлен с неверной функцией, например, setLong для строки и т. д. Попробуйте выполнить запрос со всеми параметрами, жестко закодированными в теле запроса, без каких-либо ?, чтобы увидеть, что это является проблемой.

1 голос
/ 02 января 2015

Я знаю, что это старый вопрос, но так как это один из первых результатов поиска этой проблемы, я решил опубликовать то, что сработало для меня. У меня был запрос, который занимал менее 10 секунд при использовании драйвера JDBC для SQL Server, но более 4 минут при использовании jTDS. Я перепробовал все предложения, упомянутые здесь, и ни одно из них не имело никакого значения. Единственное, что сработало, это добавление этого в URL "; prepareSQL = 1"

См. Здесь для более

0 голосов
/ 28 февраля 2019

Я знаю, что это очень старый вопрос, но так как это один из первых результатов при поиске этой проблемы, я подумал, что мне следует опубликовать то, что сработало для меня.

У меня был запрос, который занимал около 3 секунд при использовании SQL Server Management Studio (SSMS), но занимал 3,5 минуты при запуске с использованием драйвера jTDS JDBC через метод executeQuery.

Ни одно из упомянутых выше предложений не сработало для меня, главным образом потому, что я использовал только заявление, а не подготовленное заявление. Единственное, что мне помогло, - это указать имя исходной базы данных или базы данных по умолчанию в строке подключения, к которой подключающийся пользователь имеет как минимум членство в роли базы данных db_datareader . Наличие только роли public недостаточно .

Вот пример строки подключения:

jdbc:jtds:sqlserver://YourSqlServer.name:1433/DefaultDbName

Убедитесь, что в строке подключения указано окончание /DefaultDbName. Здесь DefaultDbName - это имя базы данных, с которой ID пользователя, указанный для установления соединения JDBC, имеет как минимум роль db_datareader базы данных. Если опущено, SQL Server по умолчанию использует базу данных master. Если идентификатор пользователя, использованный для установления соединения JDBC, имеет только роль public в базе данных master, запрос выполняется исключительно долго.

Я не знаю, почему это происходит. Однако я знаю, что в таких обстоятельствах используется другой план запроса. Я подтвердил это с помощью инструмента SQL Profiler.

Сведения об окружающей среде:

  • Версия SQL Server: 2016
  • Версия драйвера jTDS: 1.3.1
  • Java версия: 11
0 голосов
/ 08 февраля 2018

Иногда это может быть связано с тем, как параметры привязываются к объекту запроса. Я обнаружил, что следующий код работает очень медленно при выполнении из Java-программы.

Query query = em().createNativeQuery(queryString)                    
                .setParameter("param", SomeEnum.DELETED.name())

Как только я удаляю параметр «удаленный» и напрямую добавляю эту строку «УДАЛЕНО» к запросу, он становится супербыстрым. Это может быть связано с тем, что SQL-сервер ожидает, чтобы все параметры были связаны для определения оптимизированного плана.

0 голосов
/ 13 марта 2013

Цитата из руководств по адаптивному буферу MS:

Избегайте использования свойства строки подключения selectMethod = курсора, чтобы приложение могло обрабатывать очень большой набор результатов. Функция адаптивной буферизации позволяет приложениям обрабатывать очень большие наборы результатов только для пересылки и только для чтения без использования серверного курсора. Обратите внимание, что когда вы устанавливаете selectMethod = cursor, это влияет на все результирующие наборы только для пересылки и только для чтения, созданные этим соединением. Другими словами, если ваше приложение регулярно обрабатывает короткие результирующие наборы с несколькими строками, создание, чтение и закрытие серверного курсора для каждого результирующего набора будет использовать больше ресурсов как на стороне клиента, так и на стороне сервера, чем в случае, когда метод selectMethod курсор не установлен.

И

Есть некоторые случаи, когда использование selectMethod = курсор вместо responseBuffering = adaptive было бы более выгодным, например:

  • Если ваше приложение медленно обрабатывает набор результатов только для чтения и чтения, например, чтение каждой строки после некоторого пользовательского ввода, использование selectMethod = cursor вместо responseBuffering = adaptive может помочь сократить использование ресурсов SQL Server.

  • Если ваше приложение одновременно обрабатывает два или более набора результатов только для чтения и только для чтения на одном и том же соединении, использование selectMethod = cursor вместо responseBuffering = adaptive может помочь уменьшить объем памяти, требуемый для драйвера, в то время как обработка этих наборов результатов.

В обоих случаях вам необходимо учесть затраты на создание, чтение и закрытие серверных курсоров.

Подробнее: http://technet.microsoft.com/en-us/library/bb879937.aspx

0 голосов
/ 07 июня 2009

Попробуйте настроить размер выборки оператора и попробуйте выбрать метод курсора

http://technet.microsoft.com/en-us/library/aa342344(SQL.90).aspx

У нас были проблемы с большими наборами результатов, использующими mysql, и нам нужно было передать поток результатов, как описано в следующей ссылке.

http://helpdesk.objects.com.au/java/avoiding-outofmemoryerror-with-mysql-jdbc-driver

0 голосов
/ 07 июня 2009

Требуется ли такое же количество времени с SQLWB? Если версия Java намного медленнее, я бы проверил несколько вещей:

  1. Вы получите наилучшую производительность с помощью ResultSet только для чтения и только для чтения.
  2. Я помню, что старые драйверы JDBC от MSFT работали медленно. Убедитесь, что вы используете самую последнюю версию. Я думаю, что есть универсальный SQL Server один и один специально для SQL 2005.
...