Learning Platform
Глоссарий Troubleshooting
Урок 15.02 · 23 мин
Средний
deploymentjvmtuningperformance

JVM-тюнинг: heap, GC, headroom для воркера и координатора

Trino написан на Java и исполняется как JVM-процесс. Это значит, что производительность и стабильность кластера напрямую зависят от настроек JVM: версии Java, размера heap, выбора garbage collector. Неверно настроенная JVM — это либо OutOfMemoryError, кладущий воркеры, либо длинные GC-паузы, в которые кластер замирает.

Этот урок — про тюнинг JVM под Trino: какую именно версию Java требует сервер, как задавать heap, какой GC использовать и чем настройки координатора отличаются от настроек воркера.


Версия Java: сервер требует ровно Java 24

Начнём с жёсткого факта, который нельзя угадывать или приближать.

Сервер Trino требует ровно Java 24. Не «Java 24 или новее», не «любая свежая LTS» — Trino пинит одну конкретную мажорную версию Java для сервера. Релиз 481, который мы изучаем, требует именно Java 24. Запустить сервер Trino на Java 23 или на Java 25 не получится — пин на одну мажорную версию означает, что и более старая, и более новая Java сервером не поддерживаются.

JDBC-драйвер Trino — это отдельная история. Он требует Java 11 или новее: драйвер встраивается в чужие JVM-приложения, и для него поддерживается широкий диапазон, а не одна версия.

КомпонентТребование к Java
Сервер Trino (координатор и воркеры)ровно Java 24 — одна пинованная мажорная версия
JDBC-драйвер TrinoJava 11 или новее
DANGER

Не полагайся на память при выборе версии Java для сервера. Trino пинит конкретную мажорную версию, и она может смениться в любом релизе. Перед развёртыванием конкретной версии Trino ОБЯЗАТЕЛЬНО сверь требование к Java со страницей deployment именно того релиза, что разворачиваешь. Для релиза 481 это ровно Java 24. Поставить не ту мажорную версию — сервер не стартует.

Почему сервер пинит одну версию, а драйвер — нет? Сервер — это приложение, которое команда Trino полностью контролирует: она тестирует его против одной конкретной JVM и гарантирует поведение именно на ней, используя в том числе свежие возможности этой версии. Драйвер же живёт в чужих приложениях с разными требованиями к Java — для него поддерживают широкий диапазон.


Размер heap: свойство -Xmx

Главная цифра JVM-тюнинга — размер heap, задаётся в etc/jvm.config. Есть два способа.

Абсолютный размер через -Xmx:

-Xmx64G

Доля от доступной памяти контейнера/машины через проценты:

-XX:InitialRAMPercentage=80
-XX:MaxRAMPercentage=80

MaxRAMPercentage удобен в контейнерах: JVM сама вычисляет heap как процент от памяти, выделенной контейнеру. Один и тот же jvm.config корректно работает на подах разного размера — это особенно полезно на Kubernetes. InitialRAMPercentage, равный MaxRAMPercentage, сразу выделяет heap целиком, без постепенного роста.

Сколько heap давать? Для продакшен-воркеров — обычно десятки гигабайт и больше: 32GB как нижняя планка серьёзной нагрузки, часто существенно больше. Чем больше heap, тем больше памяти доступно под user memory запросов.

Но больше — не всегда лучше. Heap нельзя задавать «под завязку» доступной памяти:

Память ноды: heap — не вся доступная RAM
JVM heap (-Xmx)Heap, которым управляет Trino: user memory запросов, revocable memory, system-аллокации. Главная цифра тюнинга
Off-heap и сама JVMМетаданные классов, стеки потоков, прямые буферы, структуры самой JVM — это вне -Xmx
Память ОС и прочих процессовОперационной системе и другим процессам ноды тоже нужна память. Если heap занял всю RAM — нода уйдёт в своп или OOM на уровне ОС

JVM-процессу нужна память сверх heap: метаданные классов, стеки потоков, прямые буферы. И операционной системе нужен запас. Если -Xmx равен всей RAM ноды, off-heap-память JVM и ОС окажутся в дефиците — нода уйдёт в своп или будет убита OOM-killer ядра. Поэтому heap — это большая, но не вся доступная память.


Heap headroom: связь с моделью памяти

Вспомним модуль про память. Внутри heap Trino резервирует heap headroom — свойство memory.heap-headroom-per-node, по умолчанию 30% максимального heap — под system-аллокации, которые движок не контролирует резервированиями (exchange-буферы, парсинг, кэши, неточность учёта).

Здесь JVM-тюнинг и модель памяти стыкуются. Trino на старте проверяет правило:

query.max-memory-per-node + memory.heap-headroom-per-node  <  -Xmx

То есть -Xmx из jvm.config и memory-свойства из config.properties связаны и должны быть согласованы. Задав -Xmx, ты задаёшь и базу, от которой считаются дефолты query.max-memory-per-node (30% от -Xmx) и memory.heap-headroom-per-node (30% от -Xmx). При дефолтах правило выполняется автоматически: 30% + 30% = 60% < 100%. Но если эти свойства переопределять вручную, согласованность придётся держать самому — иначе Trino откажется стартовать.

WARNING

JVM-тюнинг нельзя делать в отрыве от memory-свойств. -Xmx живёт в jvm.config, а query.max-memory-per-node и memory.heap-headroom-per-node — в config.properties, но это единая система. Меняя -Xmx, держи в голове правило per-node + headroom < -Xmx. Рассогласование этих файлов — частая причина того, что нода не стартует после «безобидного» изменения heap.


Garbage collector

JVM управляет heap через garbage collector — он освобождает память от объектов, на которые больше нет ссылок. Выбор GC и его настройка задаются в etc/jvm.config.

Для Trino важно одно свойство GC — длина пауз. Когда GC выполняет stop-the-world паузу, JVM-процесс замирает целиком: воркер в этот момент не обрабатывает данные. Короткие частые паузы терпимы; длинные паузы в секунды означают, что воркер на эти секунды выпал из работы — растёт латентность запросов, в плохом случае нода выглядит «зависшей».

Поэтому под Trino используют современный low-pause garbage collector — сборщик, спроектированный держать паузы короткими даже на больших heap. Это критично, потому что Trino-воркеры работают с десятками гигабайт heap, а наивный GC на таком объёме давал бы паузы в секунды.

GC-пауза: воркер замирает на время сборки
Воркер обрабатывает данныеНормальная работа: драйверы крутятся, операторы обрабатывают Page
stop-the-world пауза GC
JVM замерла на время GCВо время stop-the-world паузы JVM-процесс замирает целиком — воркер не обрабатывает данные. Low-pause GC держит эту паузу короткой
GC завершён
Воркер продолжил работуПосле паузы обработка возобновляется. Чем длиннее паузы и чем их больше, тем выше латентность запросов
NOTE

Конкретный рекомендуемый GC и его флаги между версиями Trino могут меняться — сверяй их со страницей deployment той версии, что разворачиваешь. Принцип же неизменен: под Trino нужен GC, держащий паузы короткими на больших heap, потому что каждая длинная пауза — это секунды, на которые воркер выпал из работы.


Координатор и воркер: разные настройки

Координатор и воркер исполняют разную работу — значит, и JVM-настройки у них разные.

Воркер обрабатывает данные: хэш-таблицы join, буферы агрегаций, сортировка. Ему нужен большой heap под user memory запросов — это рабочая лошадка, и от его heap зависит, какие запросы кластер потянет.

Координатор данные не обрабатывает (при node-scheduler.include-coordinator=false). Его работа — парсинг, планирование, управление воркерами, обслуживание клиентского протокола и Web UI. Память координатора уходит на план запросов, метаданные, состояние активных запросов. Профиль нагрузки иной, и heap координатора подбирают под него, а не копируют с воркера.

АспектКоординаторВоркер
Основная работапарсинг, планирование, управлениеобработка данных
На что идёт heapпланы, метаданные, состояние запросовuser memory: join, агрегации, сортировка
Размер heapпод координациюбольшой, под нагрузку запросов
GClow-pauselow-pause
Версия Javaровно Java 24ровно Java 24

Версия Java и тип GC у них общие — это свойства платформы. А вот размер heap осмысленно различать: воркеру — щедро под обработку данных, координатору — под его собственный профиль координации.


Попробуй сам

Поэкспериментируй с JVM-настройками Trino на тестовом кластере.

  1. Проверь версию Java, на которой реально работает твой Trino: подключись по jmx-коннектору и найди MBean с информацией о рантайме, либо посмотри стартовый лог. Сверь с требованием релиза — для 481 это должна быть Java 24.
  2. В etc/jvm.config воркера поменяй -Xmx на заведомо маленькое значение (например, -Xmx1G), перезапусти и запусти тяжёлый join по tpch.sf100. Поймай поведение при нехватке heap. Затем верни вменяемый -Xmx.
  3. Сделай намеренно несогласованную конфигурацию: задай query.max-memory-per-node и memory.heap-headroom-per-node так, чтобы их сумма превысила -Xmx. Перезапусти и убедись, что Trino отказывается стартовать — прочитай текст ошибки про нарушенное правило.
  4. Через jmx-коннектор посмотри метрики GC — частоту и длительность пауз сборки мусора при нагрузке.

Цель — закрепить, что версия Java для сервера пинована и сверяется по документации, а -Xmx и memory-свойства образуют единую согласованную систему.


Проверка знанийKnowledge check
Чем требование к версии Java у сервера Trino отличается от требования у JDBC-драйвера, и почему задание -Xmx нельзя делать в отрыве от свойств query.max-memory-per-node и memory.heap-headroom-per-node?
ОтветAnswer
Сервер Trino требует ровно одну пинованную мажорную версию Java — для релиза 481 это Java 24, не «24 или новее». Сервер не запустится ни на более старой, ни на более новой мажорной версии: команда Trino полностью контролирует сервер, тестирует его против одной конкретной JVM и гарантирует поведение именно на ней, в том числе используя свежие возможности этой версии. Поэтому версию Java для сервера нельзя угадывать — её обязательно сверяют со страницей deployment конкретного релиза, так как пин может смениться. JDBC-драйвер устроен иначе: он требует Java 11 или новее — широкий диапазон, а не одну версию, потому что драйвер встраивается в чужие JVM-приложения с разными требованиями к Java. Задание -Xmx нельзя делать в отрыве от memory-свойств, потому что они образуют единую согласованную систему, хотя и лежат в разных файлах: -Xmx в jvm.config, а query.max-memory-per-node и memory.heap-headroom-per-node в config.properties. Trino на старте проверяет правило query.max-memory-per-node + memory.heap-headroom-per-node < -Xmx. Более того, дефолты обоих memory-свойств вычисляются как 30% от -Xmx, поэтому -Xmx задаёт базу для них. При дефолтах правило выполняется само (30% + 30% = 60% < 100%), но если переопределять эти свойства вручную, согласованность с -Xmx нужно держать самому — иначе Trino откажется стартовать. Меняя heap, всегда держат в голове это правило, иначе нода не поднимется после безобидного на вид изменения.

Проверьте понимание

Результат: 0 из 0
Концептуальный
Вопрос 1 из 4. Какую версию Java требует сервер Trino релиза 481?

Закончили урок?

Отметьте его как пройденный, чтобы отслеживать свой прогресс

Войдите чтобы оценить урок

Прогресс модуля
0 из 7