Java разработчики часто сталкиваются с проблемой обработки очень больших наборов данных в рамках ограничений виртуальной машины Java (JVM). Когда размер кучи значительно увеличивается — часто более чем на 32 ГБ, — время паузы при сборке мусора (GC) может увеличиваться, что приводит к снижению производительности. В этой статье рассматривается, как Chronicle Queue обеспечивает хранение и эффективный доступ к набору данных размером 1 ТБ на компьютере с 64 ГБ оперативной памяти.
Проблема больших размеров кучи
При использовании стандартных JVM, таких как Oracle HotSpot или OpenJDK, увеличение размера кучи для работы с большими наборами данных может привести к более длительным паузам при сборке мусора. Эти паузы возникают из-за того, что сборщику мусора требуется больше времени для управления более крупной кучей, что может негативно сказаться на быстродействии приложения.
Одним из решений является использование параллельного сборщика мусора, например, Azul Zing, который предназначен для работы с большими объёмами памяти и сокращает время паузы при сборке мусора. Однако этот подход может хорошо масштабироваться только в том случае, если набор данных помещается в доступную основную память.
Обработка наборов данных, размер которых превышает объем основной памяти
Что делать, если объём вашего набора данных превышает объём оперативной памяти вашего компьютера? В таких случаях использование кучи невозможно. Необходимо использовать хранилища данных вне кучи. Хотя базы данных или решения NoSQL могут обрабатывать большие наборы данных, они часто приводят к значительным задержкам, что может быть неприемлемо для высокопроизводительных приложений.
Представляем Chronicle Queue
Chronicle Queue предоставляет постоянное хранилище данных вне кучи, к которому могут обращаться несколько JVM на одном сервере. Оно использует файлы, отображаемые в памяти для эффективного управления большими наборами данных. Но как это работает, когда размер набора данных превышает доступную оперативную память?
В этой статье мы рассмотрим производительность Chronicle Queue при обработке набора данных объёмом 1 ТБ на компьютере с 64 ГБ памяти, уделяя особое внимание сценариям последовательного доступа.
Как выглядит JVM объемом 1 ТБ?
Для начала давайте посмотрим, как выглядит JVM, обрабатывающая более 1 ТБ данных, на компьютере с 64 ГБ оперативной памяти.

В верхней части вывода указано, что размер виртуальной памяти JVM (VIRT) составляет 1045,8 g , а размер резидентного набора (RES) — всего 50,0 g. Это означает, что, хотя JVM адресует более 1 ТБ данных, фактически в физическую память в любой момент времени загружается только около 50 ГБ. Это возможно благодаря тому, что Chronicle Queue использует файлы, отображаемые в памяти, что позволяет операционной системе эффективно управлять виртуальной памятью.
Анализ производительности: замедляет ли это?
Распространённая проблема заключается в том, что управление наборами данных, размер которых превышает объём основной памяти, приводит к резкому снижению производительности. Если вы попытаетесь создать кучу размером 1 ТБ на компьютере с 64 ГБ памяти, JVM, скорее всего, не сможет запуститься или сделает компьютер непригодным для использования из-за чрезмерной подкачки. Однако подход Chronicle Queue, не использующий кучу, сводит эту проблему к минимуму.
Запись и считывание Данных
В нашем тесте мы выполняем следующие шаги:
Записываем пакеты объемом 1 ГБ, состоящие из сообщений размером 1 КБ.
Выполняем операцию синхронизации для сброса данных на диск.
Последовательно считываем данные обратно.
Базовый диск может поддерживать скорость около 630 МБ/с. Запись 1 ГБ данных занимает менее 2 секунд, даже если общий размер набора данных превышает объём физической памяти.
Стабильная производительность записи

Минимальное замедление записи

Запись 1 ГБ данных занимает:
В среднем 1,3 секунды для первых 64 ГБ.
После 64 ГБ в среднем 1,5 секунды.
Небольшое увеличение времени записи минимально, учитывая размер набора данных, и приемлемо для приложений, требующих высокой пропускной способности.
Сравнение с традиционными базами данных
Рассмотрим альтернативу: вставим 1 миллион записей (примерно 1 ГБ) в традиционную базу данных и запросим их обратно. Этот процесс, скорее всего, займёт значительно больше времени и потребует больше ресурсов. С помощью Chronicle Queue вы можете прочитать 1 ГБ записей примерно за 70 мс, используя один поток, что обеспечивает значительное преимущество в производительности.
Заключение
Очередь Chronicle Queue обеспечивает исключительную производительность при работе с наборами данных, превышающими объём основной памяти. Она поддерживает низкое время паузы при сборке мусора и высокую скорость доступа к данным за счёт использования памяти вне кучи и файлов, отображаемых в память. Это делает её идеальным решением для приложений, обрабатывающих большие объёмы данных, без существенного снижения производительности.
Попробуйте Сами
Вы можете поэкспериментировать с этим тестом, используя следующий код: RunLargeQueueMain.java.
Дополнительную информацию о Chronicle Queue можно получечить в репозитории GitHub или на странице продукта Chronicle Queue.
Что предлагает корпоративная версия?
Chronicle Queue Enterprise предлагает дополнительные функции, такие как:
Репликация на нескольких серверах для обеспечения отказоустойчивости и аварийного восстановления
Реализации на C++, Rust, Python
Параметры настройки для уменьшения дрожания задержки
Поддержка Windows, macOS и Linux
Поделитесь Своими Мыслями
Сталкивались ли вы с трудностями при управлении большими наборами данных в Java? Какие решения вы считаете эффективными? Не стесняйтесь делиться своим опытом и присоединяться к обсуждению.
Примечание: в настоящее время очередь, размер которой превышает объем основной памяти, работает только в системах на базе Unix. В Windows каждый цикл (например, данные за день) должен помещаться в основную память. Планируеся устранить это ограничение в будущих обновлениях.