В далекой-далекой галактике были времена стабильности и процветания.
Сервис с шестнадцатью экземплярами работал на благо человечества.
Через Hibernate он заходил в базу данных PostgreSQL, извлекал необходимые данные и раздавал их другим через REST-интерфейс.
Однако спокойные времена прошли.
Внезапно один из экземпляров аварийно завершился с ошибкой OutOfMemoryError. Лучшие программисты hh.ru начал гоняться за дампом в поисках ценной информации.
Здравствуйте, меня зовут Артем, я бэкенд-разработчик в компании hh.ru .
В этой статье я расскажу, как мы исправили одну из ошибок OutOfMemoryError, возникавшую при работе сервиса с базой данных.
Сегодня мы говорим только на языке бэкэнда!
Расследование вел Эвокс
Мы начали расследование OOM с скачанного нами дампа памяти.Сразу обратил внимание на неестественно большой размер SessionFactory, где основной объём занимал кэш планов запросов.
Первое, что приходит на ум, это сказать «ха, это какой-то кэш, давайте его просто уменьшим и всё».
Но уменьшение кэша не решает исходную проблему, ведь если кэш есть, значит он кому-то нужен, а возможно даже и нам.
Так что же такое queryPlanCache и для чего он используется? Мы в hh.ru В качестве базы данных мы используем PostgreSQL, а в Java-коде общаемся с ним через Hibernate — библиотеку ORM (Object-Relational Mapping).
Помимо простого сопоставления объектов Java с таблицами базы данных, Hibernate предоставляет еще несколько удобных инструментов:
- Во-первых, фреймворк Criteria, который позволяет создавать запросы из чистого Java-кода, добавляя разные фильтры и логические условия.
- Во-вторых, языком написания запросов является JPQL (пришедший на смену HQL), который в большинстве случаев при работе с ORM является более удобной альтернативой чистому SQL.
Мы создали запрос в JPQL или Criteria — после этого Hibernate должен совершить какую-то магию, чтобы превратить написанное нами в SQL, понятный базе данных.
Эта магия называется «компиляция запроса».
И логично предположить, что сервис во время работы часто выполняет однотипные запросы, и каждый раз компилировать их не оптимально.
Вот почему существует queryPlanCache — это кеш скомпилированных запросов; его уменьшение = вред производительности сервиса.
При компиляции запроса Hibernate:
- анализирует исходный запрос;
- строит абстрактное синтаксическое дерево;
- запоминает тип входных и выходных параметров;
- готовит заявление, которое будет переведено в базу данных.
А вот SQL-запрос:SELECT {derived select clause} user0_.user_id as user_id1_10_ user0_.first_name as first_na2_10_, user0_.last_name as last_nam3_10_ from users user0_ where = first_name ?
select
user0_.user_id as user_id1_10_,
user0_.first_name as first_na2_10_,
user0_.last_name as last_nam3_10_
from
users user0_
where
first_name=?
Теги: #postgresql #java #hibernate #outofmemoryerror #java Season
-
Бесплатные Тетради Для Студентов.
19 Oct, 24 -
Почему Грейдл?
19 Oct, 24 -
Хорошее Плохое Интервью
19 Oct, 24 -
Короткометражный Фильм «Коляска» (3 Минуты)
19 Oct, 24 -
История О Парсинге Одного Aspx-Сайта
19 Oct, 24