Хранимый proc работает на 30% медленнее с помощью Java и работает непосредственно в базе данных

Я использую Java 1.6, JTDS 1.2.2 (также просто попробовал 1.2.4 безрезультатно) и SQL Server 2005 для создания CallableStatement для запуска хранимой процедуры (без параметров). Я вижу, что оболочка Java работает с той же самой хранимой процедурой на 30% медленнее, чем с помощью SQL Server Management Studio. Я запускаю профилировщик MS SQL, и между этими двумя процессами разница между вводами и выводами невелика, поэтому я не думаю, что это связано с кэшированием плана запроса.

Хранимый proc не принимает аргументов и не возвращает никаких данных. Он использует серверный курсор для вычисления значений, необходимых для заполнения таблицы.

Я не вижу, как вызов хранимого proc с Java должен добавить 30% накладных расходов, конечно же, это просто канал базы данных, который отправлен SQL, а затем база данных выполняет его. Может ли база данных быть давая Java-приложению другой план запроса?

Я опубликовал как форумы MSDN, так и форумы sourceforge JTDS (тема: "хранимый процесс медленнее в JTDS, чем прямой в DB" ) Мне было интересно, есть ли у кого-нибудь какие-либо предложения относительно того, почему это может произойти?

Спасибо заранее,

-Джеймс

(N.B. Не бойся, я буду сопоставлять любые ответы, которые я получаю на других форумах вместе, как только найду решение)

фрагмент кода Java:

sLogger.info("Preparing call...");
stmt = mCon.prepareCall("SP_WB200_POPULATE_TABLE_limited_rows");
sLogger.info("Call prepared.  Executing procedure...");
stmt.executeQuery();
sLogger.info("Procedure complete.");

Я запустил sql-профайлер и нашел следующее:

Java-приложение: CPU: 466,514 Считывает: 142,478,387 Пишет: 284 078 Продолжительность: 983,796

SSMS: CPU: 466,973 Считывает: 142,440,401 Пишет: 280,244 Продолжительность: 769 851

(Оба с DBCC DROPCLEANBUFFERS работают до профилирования, и оба создают правильное количество строк)

Итак, я заключил, что они оба выполняют одни и те же чтения и записи, просто так, как они это делают, разные, что вы, ребята, думаете?

Оказывается, что планы запросов существенно различаются для разных клиентов (клиент Java обновляет индекс во время вставки, который не находится в более быстром SQL-клиенте, также, как он выполняет объединения, отличается (вложенные петли Vs. собирать потоки, вложенные циклы Vs index scans, argh!)). Вполне возможно, что я еще не знаю (я буду переписывать, когда дойду до конца)

Эпилог

Я не мог заставить это работать правильно. Я попытался гомогенизировать свойства соединения (arithabort, ansi_nulls и т.д.) Между студентами студии Java и Mgmt. В итоге у двух разных клиентов были очень похожие планы запросов/выполнения (но все же с разными фактическими плагинами). Я опубликовал сводку того, что я нашел в форумах MSDN SQL Server, поскольку я нашел отличную производительность не только между клиентом JDBC и студией управления, но также между клиентом командной строки Microsoft, SQLCMD, я также проверил некоторые более радикальные вещи, такие как сетевой трафик, или обернул хранимый процесс внутри другого хранимого proc, просто для усмешек.

У меня есть ощущение, что проблема кроется где-то в том, как выполняется курсор, и это каким-то образом приводит к приостановке процесса Java, но почему другой клиент должен вызвать это другое поведение блокировки/ожидания, когда ничего else работает, и один и тот же план выполнения находится в процессе немного выше моих навыков (я не DBA!).

В результате я решил, что на 4 дня хватит времени на то, чтобы тратить время на что-то подобное, поэтому я буду неохотно координировать его (если честно, хранимая процедура требует повторного кодирования, чтобы быть более инкрементальным вместо того, чтобы каждый раз пересчитывать все данные каждую неделю), а также мелом, чтобы это произошло. Я оставлю вопрос открытым, большое спасибо всем, кто положил шляпу на ринг, все было полезно, и если кто-нибудь придумает что-нибудь еще, я бы хотел услышать еще несколько вариантов... и если кто-нибудь найдет этот пост в результате наблюдения этого поведения в их собственных средах, то, надеюсь, здесь есть несколько указателей, в которых вы можете попробовать себя, и надеяться, что мы полностью увидим дальше, чем мы.

Я готов на выходные!

-Джеймс

Ответ 1

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

Ответ 2

Вы можете присоединить Профилировщик и монитор для событий SQL: BatchCompleted и SP: Завершено с фильтром продолжительностью > 1000. Запустите процедуру с вашего Java-клиента и из SSMS. Сравните Reads и Writes двух событий (Java против SSMS). Значительно отличаются ли они? Это означало бы значительно разные пути или планы выполнения с существенной разницей в I/O.

Также попытайтесь захватить Showplan XML из двух и сравнить планы (сохранить событие в виде файла .sqlplan, открыть его в SSMS для легкого анализа). Имеют ли они подобные планы? Существуют ли дикие различия в Оценке против Фактического (строки, перемотки, повторные перемотки)? У них одинаковая степень parallelism? Планы могут быть получены из sys.dm_exec_requests.

Появляются ли какие-либо предупреждающие события, например Отсутствует статистика столбцов, Сортировка предупреждений, Предупреждение о hash, Предупреждения о выполнении, Заблокированный процесс?

Дело в том, что у вас есть в вашем распоряжении целый арсенал инструментов исследования. Как только вы найдете основную причину разницы, вы можете проследить ее до того, что отличается между настройками среды Java и средой SSMS (ADO.Net SqlClient). Такие вещи, как уровень изоляции транзакций по умолчанию, настройки ANSI и т.д.

Ответ 3

Проверка. Является ли ваша проблема тем, что два приложения (SSMS, Java) делают тот же самый идентичный вызов SQL Server, и SQL Server действует по-разному для каждого? Если это так, я ударяю такие вещи каждый год или два, и они причиняют мне боль в течение нескольких дней.

Однажды я в конечном счете изолировал каждый вызов процесса и записывал все для всего процесса в Profiler. В конце концов я заметил, что событие Login (в TextData) показало множество информации, например:

-- network protocol: TCP/IP
set quoted_identifier on
set arithabort off
set numeric_roundabort off
set ansi_warnings on
set ansi_padding on
set ansi_nulls on
set concat_null_yields_null on
set cursor_close_on_commit off
set implicit_transactions off
set language us_english
set dateformat mdy
set datefirst 7
set transaction isolation level read committed

Событие "Существующее соединение" также покажет эту информацию, но иногда посылаются сразу же последующие вызовы (партии, RPC, я сейчас чувствую) [ISQL или OSQL сделали это, я думаю], сразу reset некоторые из них - Arithabort и Quoted_Identifier, по-видимому, являются фаворитами, а другие параметры SET также изменяются в зависимости от настроек или требований любых протоколов подключения, используемых вашим интерфейсом базы данных приложений.

Другой: некоторые параметры сохраняются как атрибуты процедуры при "создании" времени, а другие учитываются во время компиляции. С одной стороны, ваши значения SET для соединения могут быть перезаписаны конфигурацией, сохраненной во время создания процедуры; с другой стороны, ваши два соединения могут отличаться настолько, что для одной процедуры создаются два плана выполнения. (Вся эта информация после достаточных исследований доступна в таблицах sys и DMV.)

Короче говоря, мне кажется, что недоумение SQL запутывает вас. По сей день я ненавижу все эти настройки goombah. Вещи ниже моего уведомления продолжают беспорядочно с ними [я имею в виду, действительно, какой дурак установил implicit_transaction для пула соединений? Но как только они это сделали...] и трудно построить структуры, когда земля (правила) продолжают меняться из-под вас. В конце концов, помните, что сказал парень о строительстве замков в болоте...

Ответ 4

Я помню, что у меня была аналогичная проблема некоторое время назад, потому что JTDS молча конвертировал строковый параметр в Unicode или что-то подобное. В результате этого преобразования SQL Server не смог использовать индекс, который использовался при запуске хранимой процедуры из SSMS.

Е.И.В.

Ответ 5

Включает ли Java-приложение передачу результатов на сервер Java (сетевые издержки) плюс некоторая обработка Java? 12-минутный запрос может дать довольно большой объем данных.

Ответ 6

Если вы смотрите на профилировщик и нет разницы между исполнением, то разница должна быть с клиентскими системами.

4 минуты похоже на то, чтобы просто подготовить инструкцию для отправки, так что 12-минутное ожидание должно вызвать какой-то другой эффект - не знаю, что это такое.

Ответ 7

Я не уверен, что это сообщение по-прежнему актуально. У нас была аналогичная проблема в нашем приложении.

Одно ключевое различие между запуском хранимой процедуры в студии управления SQL и одним запуском из JDBC - это контекст транзакции. Если вы используете ORM в Java, по умолчанию хранимая процедура выполняется в контексте транзакции. Когда вы запускаете хранимую процедуру непосредственно в студии управления SQL, транзакция отключена. Существует существенная разница в производительности.

Ответ 8

Знаете ли вы, что Microsoft отправляет JDBC-драйверы для своих баз данных?

Это может быть более результативным.

Очевидно, что вы, возможно, уже решили проблему.