Hibernate производит различный SQL для каждого запроса
Я только что протестировал свое приложение под профилировщиком и выяснил, что в строках sql используется около 30% моей памяти! Это странно.
В памяти приложения хранится много таких строк. Это SQL-запросы, сгенерированные спящим режимом, обратите внимание на разные числа и завершающие символы подчеркивания:
select avatardata0_.Id as Id4305_0_,...... where avatardata0_.Id=? for update
select avatardata0_.Id as Id4347_0_,...... where avatardata0_.Id=? for update
Вот часть, которую я не могу понять. Почему спящий режим должен генерировать разные строки sql с разными идентификаторами типа " Id4305_0 _" для каждого запроса? Почему он не может использовать одну строку запроса для всех одинаковых запросов? Это какой-то трюк, чтобы обойти кеширование запросов?
Я был бы очень признателен, если бы кто-нибудь описал меня, почему это происходит, и как избежать такого истощения ресурсов.
UPDATE
Ok. Я нашел это. Я ошибался, предполагая утечку памяти. Это была моя ошибка. Hibernate работает по назначению.
Мое приложение создало 121 (!) SessionFactories в 10 потоках, они выпустили около 2300 экземпляров SingleTableEntityPersisters. И каждый SingleTableEntityPersister генерирует около 15 SQL-запросов с разными идентификаторами. Hibernate был вынужден генерировать около 345 000 различных SQL-запросов. Все в порядке, ничего странного:)
Ответы
Ответ 1
Существует логика строки запроса, генерируемой спящим режимом. Его основная цель - получить уникальные псевдонимы для имен таблиц и столбцов.
Из вашего запроса
select avatardata0_.Id as Id4305_0_,...... where avatardata0_.Id=?
avatardata0_
== > avatardata
является псевдонимом таблицы, а 0_
добавляется, чтобы указать, что это первая таблица в запросе. Итак, если бы это была вторая таблица (или Entity) в запросе, она должна была отображаться как avatardata1_
. Он использует ту же логику для псевдонимов столбцов.
Итак, таким образом избегаются все возможные конфликты.
Вы видите эти запросы, потому что вы включаете флаг show_sql
в настройке. Предназначен для отладки запросов. После того, как приложение начнет работать, вы должны отключить его.
Подробнее о документах API здесь.
Я не очень хорошо знаю часть потребления памяти, но вы повторяете свои тесты с выключенным флагом и видите, есть ли какие-либо улучшения.
Ответ 2
Предполагая, что вы используете сервер sql, вы можете проверить объявление типа параметра для "?", убедившись, что объявление приводит к тому же объявлению с фиксированной длиной каждый раз.
Параметры динамической длины приведут к отдельным планам выполнения для каждого запроса. Это может привести к большому количеству ресурсов. То, что мы видим как ту же процедуру, интерпретируется сервером sql как другой запрос, предоставляя отдельный план выполнения.
Таким образом,
exec myprocedure @p1 varchar(3)='foo'
и
exec myprocedure @p1 varchar(6)='foobar'
приведет к разным планам. Просто из-за того, что объявления @p1 отличаются по размеру.
Об этом можно многое узнать. Если вышеизложенное относится к вам, я бы рекомендовал вам прочитать "snuffing параметра".
Ответ 3
Нет... вы можете создать общий запрос внутри спящего режима. Логика заключается в сопоставлении с таблицей и извлечении записи. Он используется для общего запроса для всей базы данных. Создайте общий запрос:
Пример:
select t.Id as Id4305_0_,...... from t where t.Id=?