История Одного Оом

В далекой-далекой галактике были времена стабильности и процветания.

Сервис с шестнадцатью экземплярами работал на благо человечества.

Через 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:

  • анализирует исходный запрос;
  • строит абстрактное синтаксическое дерево;
  • запоминает тип входных и выходных параметров;
  • готовит заявление, которое будет переведено в базу данных.

Когда мы делаем «от пользователя, где имя = :имя», Hiber строит такое дерево:
  
   

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 ?

А вот SQL-запрос:

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
Вместе с данным постом часто просматривают:

Автор Статьи


Зарегистрирован: 2019-12-10 15:07:06
Баллов опыта: 0
Всего постов на сайте: 0
Всего комментарий на сайте: 0
Dima Manisha

Dima Manisha

Эксперт Wmlog. Профессиональный веб-мастер, SEO-специалист, дизайнер, маркетолог и интернет-предприниматель.