Автор оригинала: Vlad Mihalcea.
В этой статье я собираюсь кратко изложить наиболее распространенные советы по настройке производительности гибернации, которые могут помочь вам ускорить уровень доступа к данным.
Хотя начать работу с JPA и гибернацией довольно просто, если вы хотите получить максимальную отдачу от уровня доступа к данным, очень важно понять, как работает поставщик JPA, а также свойства конфигурации, которые могут помочь вам оптимизировать производительность приложений.
НЕОБХОДИМО прочитать о @Hibernate советы по настройке производительности -> https://t.co/ot0yTLLOig Спасибо @vlad_mihalcea !
Извлечение слишком большого количества данных-проблема номер один, которая вызывает проблемы с производительностью при использовании JPA и гибернации. Это потому, что JPA позволяет очень легко получить больше данных, чем вам действительно нужно.
С самого начала вам следует предпочесть использование ленивой выборки и иметь в виду, что @manytoon
и @OneToOne
ассоциации по умолчанию извлекаются с нетерпением. При использовании режима гибернации невозможно переключить стратегию извлечения с “НЕТЕРПЕЛИВОГО” на “ЛЕНИВЫЙ”, даже если вы используете графики сущностей JPA .
Более того, если вы забудете ПРИСОЕДИНИТЬСЯ к ИЗВЛЕЧЕНИЮ ЖЕЛАЕМОЙ ассоциации в запросе JPQL или API критериев, у вас возникнет проблема с запросом N+1 .
Для получения более подробной информации о том, почему вы должны предпочесть медленную загрузку, ознакомьтесь с этой статьей .
Еще одним очень важным аспектом при извлечении данных с помощью JPA и Hibernate является различие вариантов использования, в которых требуются сущности, по сравнению с теми, которые могут отлично работать с проекцией DTO. Как правило, если вы хотите ВСТАВЛЯТЬ, ОБНОВЛЯТЬ или УДАЛЯТЬ записи, извлечение объектов очень удобно, особенно благодаря механизму автоматической проверки на наличие ошибок .
Однако, если вам нужно только отобразить данные (например, таблицу, деревья), и вы не хотите их дополнительно изменять, то точечная проекция гораздо более подходит . В отличие от выборки сущностей, проекция данных позволяет определить количество столбцов, которые вы извлекаете из базы данных, и это может значительно ускорить запросы.
Мало того, что вы должны учитывать количество столбцов, которые вы извлекаете из базы данных, но вы также должны ограничить количество записей. Если данные предназначены для отображения в пользовательском интерфейсе, то уже существует ограничение на объем данных, которые вы можете отображать в одном представлении, поэтому все остальное становится ненужным, что влияет на производительность приложения. Кроме того, данные имеют тенденцию расти со временем, и если вы не ограничиваете наборы результатов запроса, объем извлекаемых данных будет увеличиваться и увеличиваться. Если вам нужно предсказуемое время отклика, лучше всего ограничить наборы результатов запроса.
Для получения более подробной информации о том, как работает разбиение запросов на страницы и почему оно обеспечивает лучшие планы выполнения SQL, ознакомьтесь с этой статьей .
При смешивании ОБЪЕДИНЕНИЯ ВЫБОРКИ
и разбиения на страницы вы можете столкнуться с HHH000104: firstResult/maxResults, заданный при выборке коллекции; применение в памяти
вопрос. Чтобы устранить эту проблему, ознакомьтесь с этой статьей .
В результате несоблюдения предыдущего совета вы можете столкнуться с различными анти-шаблонами архитектуры приложений, такими как Открытый сеанс в представлении или Временный сеанс.
Открытый сеанс в режиме просмотра (OSIV) сохранит сеанс гибернации открытым даже после выхода за пределы уровня транзакционного обслуживания. Хотя это предотвратит исключение LazyInitializationException, цена производительности будет значительной, поскольку для каждой дополнительной инициализации прокси-сервера без транзакций потребуется новое подключение к базе данных, что окажет давление на базовый пул соединений. Для получения более подробной информации о том, почему вы всегда должны избегать проектирования архитектуры OpenSessionInView, ознакомьтесь с этой статьей .
Другим вариантом антишаблона OSIV является антишаблон временного сеанса, который на самом деле даже хуже, чем OSIV, поскольку он не только использует новое подключение к базе данных для каждой новой инициализации прокси-сервера, но также требует открытия нового сеанса гибернации. Для получения более подробной информации об этом анти-шаблоне ознакомьтесь с этой статьей .
Хотя поддержка Java 1.8 Stream
очень удобна для обработки сбора данных в памяти, это не обязательно верно для потоковой передачи данных, поступающих из системы реляционных баз данных. JPA 2.2 даже добавил getResultStream
на javax.постоянство. Запрос
объект, который позволяет возвращать Поток
вместо Списка
.
За кулисами Hibernate уже давно поддерживает набор результатов
потоковую передачу с помощью прокрутки
метода org.hibernate.запрос. Запрос
объект, который полагается на JDBC Набор результатов
прокрутка. Однако прокрутка не так тривиальна, как можно было бы подумать.
Во-первых, не все драйверы JDBC прибегают к прокрутке при настройке FetchSize
свойства для базового Оператора
или prepareStatement
объекта. Например, в MySQL, чтобы активировать прокрутку набора результатов, вам нужно либо установить Оператор
размер выборки в Целое число. Значение MIN_VALUE
или установите для него положительное целое значение, одновременно установив для свойства useCursorFetch
подключение значение true
.
Более того, как объяснено в этой статье , прокрутка Набор результатов
работает хуже, чем по умолчанию только для переадресации Набор результатов
.
Более того, как объясняется в этой статье , запрос прокрутки может не предоставить максимальный набор результатов для базы данных, что может привести к тому, что база данных выберет сканирование всей таблицы вместо сканирования индекса, даже если приложению требуется небольшое количество записей из базового набора результатов
.
В отличие от курсоров или потоков базы данных, Hibernate просматривает весь набор результатов JDBC и создает список объектов сущностей или DTOS. Количество циклов, необходимых для извлечения всего
результирующего набора , определяется свойством
FetchSize JDBC
Заявление или
Подготовленное заявление объекты.
При использовании PostgreSQL или MySQL вам не нужно беспокоиться о размере выборки, так как драйвер JDBC кэширует весь набор результатов заранее, поэтому существует единая база данных, позволяющая материализовать набор результатов и сделать его доступным для приложения.
Однако при использовании Oracle размер выборки по умолчанию составляет всего 10 , что означает, что для извлечения 100 записей требуется 10 поездок туда и обратно. При использовании Hibernate вы можете легко увеличить размер выборки каждого Подготовленного оператора
с помощью свойства конфигурации hibernate.jdbc.fetch_size
. Для получения более подробной информации о размере выборки оператора JDBC ознакомьтесь с этой статьей .
По умолчанию все запросы сущностей JPA и Hibernate выполняются в режиме чтения-записи, что означает, что возвращаемые сущности управляются текущим контекстом сохранения, поэтому изменения состояния сущностей будут обнаружены и переведены в инструкцию UPDATE SQL.
Однако вы не хотите изменять возвращаемые сущности, гораздо лучше извлекать сущности в режиме только для чтения. Это позволит Hibernate отказаться от связанного отключенного состояния, которое используется механизмом проверки на загрязнение для обнаружения изменений состояния объекта. Более того, объекты, доступные только для чтения, пропускаются во время очистки.
Для извлечения сущностей в режиме только для чтения вы можете сделать это либо на уровне Сеанса
, как показано в следующем примере:
Session session = entityManager.unwrap(Session.class); session.setDefaultReadOnly(true);
или Запрос
уровень:
Listposts = entityManager.createQuery( "select p from Post p", Post.class) .setHint(QueryHints.HINT_READONLY, true) .getResultList();
Извлекая объекты в режиме только для чтения, вы уменьшите выделение памяти, поскольку состояние отсоединения больше не сохраняется контекстом сохранения. Имея меньше Объекты Java для удаления, стратегия только для чтения также более эффективна с точки зрения сборщика мусора. Таким образом, эта стратегия экономит больше, чем просто память. Это также экономит циклы процессора, которые в противном случае были бы потрачены на сбор объектов массива отделенных состояний после закрытия текущего контекста сохранения.
Потрясающая коллекция советов по производительности Hibernate с множеством ссылок на другие статьи по теме. Спасибо @vlad_mihalcea !
В то время как кэширование операторов обрабатывается базовым драйвером JDBC
, платформа доступа к данным также может помочь повысить вероятность попадания в кэш операторов.
Прежде всего. Hibernate выполняет все SQL-запросы и операции DML с использованием подготовленных инструкций. Подготовленные инструкции не только помогают предотвратить атаки с использованием SQL-инъекций , но и могут ускорить выполнение запросов, особенно когда базовая база данных предоставляет кэш плана выполнения (например, Oracle, SQL Server).
Hibernate также предлагает hibernate.запрос.in_clause_parameter_padding
свойство конфигурации. Для получения более подробной информации ознакомьтесь с этой статьей .
Обратите внимание, что некоторые драйверы JDBC эмулируют подготовленные инструкции даже при использовании JDBC Подготовленное заявление
объект. Для получения более подробной информации ознакомьтесь с этой статьей .
Еще одной оптимизацией, добавленной Hibernate, которая помогает повторно использовать данный план выполнения, является режим буквальной обработки API настраиваемых критериев. Традиционно числовые литералы были встроены, в то время как строковые литералы предоставлялись в качестве подготовленных параметров привязки оператора. С помощью hibernate.criteries.literal_handling_mode
теперь вы можете выбрать привязку всех литералов, что увеличивает вероятность попадания в кэш операторов. Для получения более подробной информации о режиме обработки литералов критериев ознакомьтесь с этой статьей .
Когда дело доходит до пакетной обработки, Hibernate предлагает несколько вариантов оптимизации. Прежде всего, контекст сохранения действует как транзакционный кэш для записи с обратной связью . Кэш с последующей записью позволяет Hibernate задерживать выполнение оператора до времени сеанса
сброса, что дает возможность группировать операторы одного и того же типа в пакеты.
При выполнении пакетной обработки с помощью Hibernate общеизвестно, что контекст сохранения необходимо периодически очищать и очищать, чтобы избежать нехватки памяти и увеличить время очистки из-за обработки все большего количества объектов при каждом вызове очистки. Однако менее очевидно, что транзакцию с базой данных также стоит периодически совершать, особенно при обработке больших объемов данных. Это может помочь вам избежать длительных транзакций, а также потери всей выполненной работы только из-за одной ошибки в конце пакета. Для получения более подробной информации о наилучшем способе пакетной обработки с помощью JPA и гибернации ознакомьтесь с этой статьей .
Чтобы включить пакетирование JDBC, вам нужно только установить свойство hibernate.jdbc.batch_size
конфигурации, и Hibernate автоматически переключится на использование пакетной обработки инструкций JDBC. Это очень удобно, так как большинство приложений не пишутся с учетом пакетной обработки, и переход от пакетной обработки к пакетной может потребовать переписывания всего уровня доступа к данным в случае, если базовая платформа предлагает другой API для использования пакетной обработки.
Помимо свойства конфигурации SessionFactory
уровня, вы также можете использовать размер пакета JDBC уровня сеанса
, поэтому выберите правильный размер пакета для каждого бизнес-варианта использования. Для получения более подробной информации ознакомьтесь с этой статьей .
При пакетной обработке инструкций INSERT и UPDATE, помимо свойства hibernate.jdbc.batch_size
configuration, следует также рассмотреть возможность включения следующих двух свойств:
Эти два свойства позволяют Hibernate изменять порядок операторов таким образом, чтобы операторы одного и того же типа выполнялись пакетами, а не чередовались с другими операторами. Для получения более подробной информации ознакомьтесь с этой статьей .
Хотя Hibernate 4 и 5 не предоставляют возможности заказывать инструкции по удалению, вы можете обойти это ограничение, как описано в этой статье .
Помимо всех пакетных оптимизаций для гибернации, вы также можете воспользоваться преимуществами базового драйвера JDBC. Например, PostgreSQL позволяет группировать операторы SQL, используя режим Перезапись пакетных вставок
. Для получения более подробной информации об этом свойстве ознакомьтесь с этой статьей .
Получение соединения с базой данных-дорогостоящая операция, и именно поэтому рекомендуется использовать метод объединения соединений в пул . Hibernate предлагает несколько интеграций пулов соединений: Хикари, Вибур DBCP, c3p0.
Однако лучший способ интегрировать решение для объединения в пул с Hibernate-использовать внешний Источник данных
и предоставить его через свойство конфигурации hibernate.connection.datasource
. Таким образом, вы не только можете использовать любое решение для пула соединений, но и можете интегрировать решение для мониторинга пула соединений, например FlexyPool .
Помимо объединения пулов соединений, при использовании режима гибернации необходимо учитывать два аспекта:
- получение соединения
- освобождение соединения
Для транзакций JTA соединения приобретаются лениво перед выполнением запроса или перед очисткой контекста сохранения. Для транзакций RESOURCE_LOCAL подключение к базе данных приобретается правильно при запуске транзакции JPA, поскольку Hibernate необходимо убедиться, что флаг автоматической фиксации отключен на базовом соединении JDBC . Если пул соединений уже отключает режим автоматической фиксации, вы можете указать Hibernate, чтобы избежать быстрого получения соединения через
hibernate.connection.свойство provider_disables_autocommit
подключение. Для получения более подробной информации ознакомьтесь с этой статьей .
Когда дело доходит до освобождения соединений, транзакция RESOURCE_LOCAL вернет соединение пулу после фиксации или отката текущей текущей транзакции. Для транзакций JTA соединение освобождается после каждой инструкции, только для того, чтобы быть приобретенным снова до выполнения новой инструкции. Поскольку этот процесс может привести к дополнительным накладным расходам, стоит установить hibernate.connection.release_mode
свойство подключения к after_transaction
если менеджер транзакций JTA правильно работает в этом режиме. Для получения более подробной информации ознакомьтесь с этой статьей .
Хотя Hibernate может регистрировать операторы SQL, установив соответствующее приложение журнала, гораздо лучше делегировать эту ответственность JDBC источнику данных
или Драйвер
прокси-решение с возможностями ведения журнала, как описано в этой статье . Мало того, что вы можете регистрировать значения параметров привязки в исполняемом операторе SQL, но вы можете печатать, если используется пакетирование, а также время выполнения оператора.
Более того, при использовании такого инструмента, как источник данных-прокси
, вы можете утверждать количество операторов, которые Hibernate генерирует от вашего имени, тем самым предотвращая N+1 проблем с запросами во время тестирования задолго до того, как они станут проблемой в производстве.
При использовании JPA и гибернации необходимо обращать внимание при сопоставлении объектов, так как это может повлиять на производительность приложения. Как правило, важно использовать очень компактные столбцы на стороне базы данных, чтобы уменьшить объем диска и памяти.
Для идентификаторов генератор последовательностей работает лучше всего, особенно при использовании с объединенными или объединенными оптимизаторами .
Генератор идентификаторов, хотя и является жизнеспособной альтернативой с точки зрения базы данных, заставляет Hibernate упускать возможность пакетной обработки операторов во время сброса, поскольку к тому времени, когда Hibernate пытается сгруппировать операторы ВСТАВКИ, операторы уже выполняются для того, чтобы Hibernate извлек идентификатор сущности.
Генератор ТАБЛИЦА является худшим выбором , и его следует избегать. Если переносимость является единственной причиной, по которой вы выбрали генератор ТАБЛИЦЫ
, вам лучше использовать ПОСЛЕДОВАТЕЛЬНОСТЬ
по умолчанию и переопределить стратегию идентификаторов во время сборки с помощью orm.xml
Файл конфигурации JPA, как описано в этой статье .
Обратите внимание на генератор АВТО
идентификаторов в MySQL и MariaDB до версии 10.3, так как по умолчанию используется генератор ТАБЛИЦ , который работает плохо и может привести к узким местам в производительности.
Для ассоциации картинка стоит 1000 слов:
Для получения более подробной информации ознакомьтесь со следующими статьями:
- Лучший способ сопоставить отношения @OneToMany с JPA и гибернацией
- Лучший способ сопоставить отношения @OneToOne с JPA и гибернацией
- Лучший способ использовать аннотацию @ManyToMany с JPA и гибернацией
- Лучший способ сопоставить связь “многие ко многим” с дополнительными столбцами при использовании JPA и Hibernate
Хотя Hibernate подходит для случаев использования OLTP, если вы хотите обрабатывать большие объемы данных, не стоит перемещать все данные из базы данных по сети в кучу JVM только для обработки на прикладном уровне.
Если вы хотите обновить или удалить записи, соответствующие заданной логике фильтрации, вам лучше использовать оператор bulk. Вы даже можете изменить логику фильтрации инструкции массового обновления или удаления с помощью API критериев, как описано в этой статье .
Для более сложных сценариев обработки можно использовать хранимые процедуры, как описано в следующих статьях:
- Как вызывать хранимые процедуры и функции Oracle с помощью JPA и Hibernate
- Как вызывать хранимые процедуры и функции SQL Server с помощью JPA и Hibernate
- Как вызывать функции PostgreSQL (хранимые процедуры) с помощью JPA и Hibernate
- Как вызывать хранимые процедуры и функции MySQL с помощью JPA и Hibernate
Еще один холодный зимний уик-энд. Идеальное время, чтобы потратить некоторое время на то, чтобы отточить наш #Спящий режим / #JPA #производительность навыки с @vlad_mihalcea . Обязательно прочитайте, что я рекомендую всем своим слушателям: Обязательно прочитайте, что я рекомендую всем своим слушателям:
Хотя Hibernate предоставляет кэш второго уровня, прежде чем принимать решение об его использовании, вам лучше правильно настроить сервер базы данных, чтобы пул буферов или общие буферы могли хранить рабочий набор в памяти и, следовательно, избегать загрузки слишком большого количества страниц данных с диска.
Кроме того, если ваше приложение в основном использует трафик для чтения, репликация базы данных является очень эффективным способом увеличения нагрузки на входящий трафик.
С другой стороны, кэш второго уровня может быть хорошим подходом для разгрузки основного узла даже при использовании репликации базы данных.
Для получения более подробной информации о том, как использовать кэш 2-го уровня Hibernate, ознакомьтесь с этими статьями:
- Как в режиме гибернации хранятся записи кэша второго уровня
- Как работает стратегия Hibernate только для чтения CacheConcurrencyStrategy
- Как работает стратегия Hibernate NONSTRICT_READ_WRITE CacheConcurrencyStrategy
- Как работает стратегия Hibernate READ_WRITE CacheConcurrencyStrategy
- Как работает транзакционная стратегия CacheConcurrencyStrategy в режиме гибернации
- Как работает кэш коллекции Hibernate
- Как работает кэш запросов в режиме гибернации
- Как использовать кэш запросов Hibernate для проекций DTO
- Как избежать проблемы с кэшированием запросов в режиме гибернации N+1
- Как кэшировать несуществующие результаты выборки сущностей с помощью JPA и гибернации
Другой менее известной темой при настройке режима гибернации является кэш плана запросов. Все запросы сущностей (например, JPQL или API критериев) необходимо проанализировать, чтобы сгенерировать соответствующую инструкцию SQL. Этот процесс анализа запроса сущности требует времени, поэтому Hibernate предлагает кэш плана для повторного использования уже вычисленного плана.
Если ваше приложение генерирует много запросов, важно правильно настроить кэш плана запросов. Для получения более подробной информации ознакомьтесь с этой статьей .
Если вы используете JPA и спящий режим, вы можете многое сделать, чтобы ускорить уровень доступа к данным. Следуя советам, приведенным в этой статье, вы получите лучшее представление о том, как работает режим гибернации, чтобы можно было разработать приложение таким образом, чтобы максимально использовать базовую базу данных, драйвер JDBC и реализацию JPA.