2010-04-13 5 views
5

У нас есть веб-приложение, работающее в производственной среде, и в какой-то момент клиент пожаловался на то, насколько медленным было приложение.Как избежать этого очень тяжелого запроса, замедляющего приложение?

Когда мы проверили, что происходит с приложением и базой данных мы открываем этот «драгоценный» запрос, который в настоящее время выполняется несколько пользователей одновременно (таким образом, нанося чрезвычайно высокой нагрузкой на сервере базы данных):

SELECT NULL AS table_cat, 
     o.owner AS table_schem, 
     o.object_name AS table_name, 
     o.object_type AS table_type, 
     NULL AS remarks 
FROM  all_objects o 
WHERE o.owner LIKE :1 ESCAPE :"SYS_B_0" AND 
     o.object_name LIKE :2 ESCAPE :"SYS_B_1" AND 
     o.object_type IN(:"SYS_B_2", :"SYS_B_3") 
ORDER BY table_type, table_schem, table_name 

Наше приложение не выполняет этот запрос, я считаю, что это внутренний запрос Hibernate. Я нашел мало информации о том, почему Hibernate делает этот чрезвычайно тяжелый запрос, поэтому любая помощь в том, как его избежать, очень ценится!

Информация о производственной среде: Red Hat Enterprise Linux 5.3 (Tikanga), JDK 1.5, веб-контейнер OC4J (белый сервер приложений Oracle), Oracle Database 10.1.0.4, драйвер JDBC для JDK 1.2 и 1.3, версия Hibernate 3.2.6 .ga, библиотека пула соединений C3P0 версия 0.9.1.

UPDATE: Благодаря @BalusC для claryfing, что на самом деле это Hibernate, который выполняет запрос, теперь у меня есть лучшее представление о том, что происходит. Я объясню, как мы обрабатываем сессию спящего режима (это очень рудиментарно, да, если у вас есть предложения о том, как с этим справиться лучше, они более чем приветствуются!)

У нас есть фильтр (реализует javax.servlet.Filter) что когда он запускается (метод init), он строит фабрику сеансов (судорожно это происходит только один раз). Затем каждый HttpRequest, который идет в приложение, проходит через фильтр и получает сеанс , и он начинает транзакцию. Когда процесс завершен, он возвращается через фильтр, совершает фиксацию транзакции, убивает сеанс hibernate, затем переходит к передовой странице (мы не храним сеанс hibernate в сеансе Http, потому что он никогда не работал хорошо в наших тестах).

Теперь вот часть, где я думаю, проблема. В нашей среде разработки мы развертываем наши приложения в Tomcat 5.5, и когда мы запускаем сервис, все фильтры запускаются сразу и только один раз. В производственной среде с OC4J, похоже, не работает. Мы развертываем приложение, и только когда приходит первый запрос, OC4J создает фильтры.

Это заставляет меня думать, что OC4J конкретизирует фильтры на каждый запрос (или по крайней мере несколько раз, что до сих пор не так), создавая тем самым сессионный завод по каждому запросу, которым выполняется, что% &% #% $ # query, что приводит к моей проблеме!

Теперь, это правильно? У меня есть способ настроить OC4J для того, чтобы он создавал фильтры только один раз?

Большое спасибо всем вам за то, что нашли время, чтобы ответить на это!

+0

Вы, ребята, решили изменить приложение таким образом, чтобы результат этого запроса был кеширован? результатом этого является статическая информация, если вы не создаете инструмент управления БД. –

+0

Также вы используете правильный диалект Hibernate и правильные драйверы JDBC Oracle? – aperkins

+0

Я уверен, что мы используем правильный диалект, а не только драйвер (но он никогда не давал проблем раньше). И запрос не является частью приложения, это должен быть запрос на спящий режим, c3p0 или драйвер, мы не уверены, кто его выполняет. Сначала я подумал, что это водитель, так что да, мы устанавливаем последнюю. –

ответ

2

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

Что происходит, так это то, что приложения Oracle Forms занимались блокировкой записей в базе данных, поэтому очень медленно выполняли всю работу базы данных (включая мой любимый запрос Hibernate).

Причина блокировок заключалась в том, что ни один из внешних ключей приложений Oracle Forms не был проиндексирован. Так как мой босс объяснил мне это (он обнаружил причину), когда пользователь редактирует основную запись отношения «мастер-деталь» в приложении Oracle Form, база данных блокирует всю таблицу подробностей, если нет индекса для ее внешний ключ. Это связано с тем, что Oracle Forms работает, что он обновляет все поля основной записи, в том числе и первичного ключа, которые являются полями, которые ссылаются на внешний ключ.

Короче говоря, НИКОГДА не оставляйте свои внешние ключи без указателей. Мы много страдали от этого.

Спасибо всем, кто нашел время, чтобы помочь.

+0

Вы имеете в виду, что поле внешнего ключа, на которое делается ссылка, индексируется, правильно? – rogerdpack

0

Я считаю, что этот запрос исходит от драйвера JDBC Oracle, чтобы реализовать запрос Hibernate для извлечения информации о объекте базы данных через DatabaseMetaData.

Этот запрос не должен быть слишком дорогим или, по крайней мере, не в моей системе. Каков ваш счет all_objects и что еще более важно, что вы видите в итогах строк/байтов для плана объяснения?

+0

Зависит от переменных привязки. Подстановочный знак для владельца и имени объекта может возвращать большое количество данных. –

+0

Это невозможно сделать из драйвера JDBC, поскольку он ничего не знает о Hibernate. – BalusC

+0

... не Java-программист, но то, что я пытался сказать, что Hibernate вызывал какой-то метод класса DatabaseMetaData, который является частью драйвера, с целью получения информации о таблице/представлении - теперь я вижу более знающих Вопросы, связанные с Hibernate, начиная с моего исходного сообщения – dpbradley

1

Является ли схема sys в вашей базе данных 10g анализируемой обновленной статистикой? Вы собрали статистику по фиксированным таблицам в схеме sys. Запросы по всем_объектам не должны обладать налогом на систему. Если вы запустили запрос через autotrace/tkprof, то что/где будет основным из ресурсов, которые будут потрачены.

+0

У нас есть много объектов в базе данных на purpouse (есть другое приложение, разработанное в PL/SQL), поэтому для этого запроса требуется много времени для выполнения (и, кроме того, время, когда мы его обнаружили, одновременно было 17 пользователей). И снова этот запрос НЕ является частью нашего веб-приложения, должен быть драйвером Hibernate/C3P0/oracle jdbc, мы пока не выяснили его. –

3

Это действительно происходит из Hibernate и, в частности, org.hibernate.tool.hbm2ddl.TableMetadata. Под каждым из них были использованы для проверки схемы (сопоставление таблиц и столбцов). По-видимому, это излишне выполнялось на каждом порожденном запросе или сеансе, а не только один раз во время запуска приложения. Вы, например, не имеете ненужного вызова Hibernate Configurator для каждого запроса или сеанса?

+0

На самом деле этот класс является частью Hibernate Core. –

+0

Да? ОК, пересмотрено. – BalusC

+1

Да, упаковка может вводить в заблуждение, но она является частью 'hibernate-core.jar'. –

2

Как указал @BalusC, этот запрос выполняется во время проверки схемы. Но валидация обычно выполняется один раз для всех при создании SessionFactory (если активировано). Вы вызываете следующий метод явно: Configuration#validateSchema(Dialect, DatabaseMetadata)?


Теперь, это правильно? У меня есть способ настроить OC4J для того, чтобы он создавал фильтры только один раз?

Ваша реализация открытого сеанса In View выглядит прекрасно (и очень близка к той, которая предложена в this page). И в соответствии с спецификацией Servlet только один экземпляр за <filter> декларация в дескрипторе развертывания создается на виртуальную машину Java (JVMTM) контейнера. Поскольку маловероятно, что это не так с OC4J, я испытываю соблазн сказать, что должно быть что-то еще.

Можете ли вы поместить некоторые данные в фильтр? Как сделать статический SessionFactory (в старом классе HibernateUtil)?

+0

Извините, я видел ваш ответ * после * Я обновил свой ответ с мыслью о конфигурации Hibernate, но это действительно первое направление, в котором OP должен искать причину и решение :) (+ 1) – BalusC

1

Это исходит из тестового запроса C3PO по умолчанию. Предоставьте более простой запрос в вашей конфигурации. Что-то вроде, выберите «X» из дуального.

2

В частности, происходит то, что люди, которые пишут программное обеспечение, поддерживающее разные базы данных, упаковывают свое программное обеспечение в базу данных нейтральным образом. то есть. когда переопределения нет, то они используют jdbc db metadata getTables, чтобы проверить, действительно ли соединение действительно. Обычно вы переопределяете с помощью select * from dual и т. Д., Но когда это не сделано или вы конкретно не говорите, какую базу данных вы используете, программное обеспечение написано для запуска чего-то, что будет работать с любым драйвером JDBC. База данных метаданных jdbc db getTables сделает это.

2

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

То, что я сделал, переопределяет вид ALL_OBJECTS в подключенной схеме, так что он возвращает только собственные объекты, а не все объекты в db.

например.

СОЗДАТЬ ИЛИ ЗАМЕНИТЬ ПРОСМОТР ВСЕХ ОБЪЕКТОВ КАК ВЫБРАТЬ ПОЛЬЗОВАТЕЛЯ, O. * FROM USER_OBJECTS O;

Это не самое лучшее решение, но для этого приложения нет ничего, что бы использовало представление ALL_OBJECTS, поэтому оно отлично работает и запускается значительно быстрее.

+0

Спасибо! Это, безусловно, обходной путь, который лучше всего избегать в производстве, но он отлично работает в нашей тестовой среде. – Siggen

+0

Спасибо! Так оно и было - мое время запроса изменилось с минут на несколько секунд! – Grzegorz

1

Если бы та же самая проблема, причина была именно та, что описана Бобом Breitling, C3P0 использует по умолчанию JDBC API для тестирования соединения:

java.sql.DatabaseMetaData#getTables(....) 

Чтобы изменить это поведение по preferredTestQuery должен быть установлен , или если C3P0 используется через спящий режим - hibernate.c3p0.preferredTestQuery