Сможет ли Spring Batch предотвратить остановку моей программы на 94 миллионах транзакций, если сборка мусора является проблемой? - PullRequest
0 голосов
/ 08 января 2019

Это может выглядеть как вопрос, подобный Оптимизация производительности для обработки 115 миллионов записей для вставки в Oracle , но я чувствую, что это другая проблема, и на другой вопрос нет однозначного ответа из-за некоторых отсутствие ясности.

Я загружаю файл netCDF, состоящий из следующих переменных и измерений, в три таблицы в базе данных для сбора данных из нескольких источников данных

Variables:
Time: 365 entries in hours since Jan 1, 1900
Latitude: 360 entries, center of 1/2 degree latitude bands
Longitude: 720 entries, center of 1/2 degree longitude bands
Precipitation: 3 Dimensional Array Time, Lat, Lon in dimensions

Три таблицы, которые я строю, выглядят так:

UpdateLog:
uid    year    updateTime

Location:
lid    lat    lon

(hidden MtM table) UpdateLog_Location:
uid    lid

Precipitation:
pid    lid    uid    month    day    amount

Если вы выполните математику, в Location (и скрытой таблице) будет около 250 тыс. Записей для каждого этого файла (это только год 2017), а в таблице Precipitation - до 94 млн. Записей.

Сейчас я просто использую Spring Boot, пытаюсь прочитать данные и обновить таблицы, начиная с Location.

Когда у меня размер пакета 1, база данных начала обновляться довольно быстро, но со временем зависла. В то время у меня не было настроено профилирование, поэтому я не был уверен, почему.

Когда я установил его на 500, я начал четко замечать шаги, поскольку он замедлял каждое обновление, но он начинался намного быстрее, чем размер пакета 1. 1. 1017 *

Я установил 250 000, и он обновил первые 250 000 записей примерно за 3 минуты, когда при размере партии в 1, 72 часа даже близко не подошли. Однако я начал профилировать программу и кое-что заметил. Похоже, что это проблема не с базой данных (35-40 секунд - все, что требуется для фиксации всех этих записей), а с Java, так как кажется, что Сборка мусора не поспевает за всеми старыми POJO.

Теперь я рассмотрел 2 возможных решения этой проблемы. Spring Batch, и просто прямой импорт CSV в MariaDB. Я бы предпочел сделать первое, если это возможно, чтобы сохранить единство, если это возможно. Однако я заметил, что Spring Batch также заставляет меня создавать POJO для каждого из элементов.

Решит ли Spring Batch эту проблему для меня? Можно ли это исправить с помощью диспетчера потоков и многопоточности операции, чтобы можно было запускать несколько ГХ одновременно? Или мне просто сделать прямой импорт файла CSV в MariaDB?

Проблема в том, что даже если я смогу сделать этот один файл за несколько дней, мы создадим базу данных с исторической погодой всех типов. Будет еще много файлов для импорта, и я хочу создать работоспособную структуру, которую мы можем использовать для каждого из них. Для этого единственного источника данных есть еще 116 лет данных!

Редактировать: Добавление некоторых показателей из прогона прошлой ночью, которые подтверждают мое убеждение, что проблема в сборке мусора.

194880 nanoseconds spent acquiring 1 JDBC connections;
0 nanoseconds spent releasing 0 JDBC connections;
1165541217 nanoseconds spent preparing 518405 JDBC statements;
60891115221 nanoseconds spent executing 518403 JDBC statements;
2167044053 nanoseconds spent executing 2 JDBC batches;
0 nanoseconds spent performing 0 L2C puts;
0 nanoseconds spent performing 0 L2C hits;
0 nanoseconds spent performing 0 L2C misses;
6042527312343 nanoseconds spent executing 259203 flushes (flushing a total of 2301027603 entities and 4602055206 collections);
5673283917906 nanoseconds spent executing 259202 partial-flushes (flushing a total of 2300518401 entities and 2300518401 collections)

Как вы можете видеть, он тратит на 2 порядка больше времени на очистку памяти, чем на самом деле.

Ответы [ 3 ]

0 голосов
/ 08 января 2019

Я заметил, что Spring Batch также заставляет меня создавать POJO для каждого из элементов.

Spring Batch не заставляет вас анализировать данные и отображать их в POJO. Вы можете использовать PassThroughLineMapper и обрабатывать элементы в их необработанном формате (даже в двоичном, если хотите).

Я бы порекомендовал использовать разбиение в вашем случае.

0 голосов
/ 15 января 2019

Я хотел бы поблагодарить тех, кто помог мне, так как я нашел несколько ответов на свой вопрос, и я обрисую их здесь.

Проблема возникла из-за того, что Hibernate в итоге создает 1000 заданий по сборке мусора на POJO и не очень хорошая система для пакетной обработки. Любое хорошее средство для больших партий позволит вообще не использовать Hibernate.

Первый метод, который я обнаружил, использует Spring Boot без Hibernate. Создав собственный метод массового сохранения в интерфейсе репозитория, я смог напрямую связать его с запросом вставки SQL без необходимости использования POJO или использования спящего режима для создания запроса. Вот пример того, как это сделать:

@Query(value = "insert ignore into location (latitude, longitude) values(:latitude, :longitude)",
       nativeQuery = true)
public void bulkSave(@Param("latitude") float latitude, @Param("longitude") float longitude);

Это значительно уменьшило накладные расходы на сборку мусора, позволяя процессу выполняться без замедления со временем. Тем не менее, для моих целей, хотя и на порядок быстрее, это все равно было слишком медленно для моих целей, занимая 3 дня для 94 миллионов строк.

Другим показанным мне способом было использование Spring Batch для массовой отправки запросов вместо отправки по одному. Из-за моего необычного источника данных это был не простой файл, мне пришлось обрабатывать данные и подавать их в ItemReader по одной записи за раз, чтобы создать впечатление, что они исходят из файла напрямую. Это также улучшило скорость, но я нашел гораздо более быстрый метод, прежде чем пытался это сделать.

Самый быстрый способ, который я нашел, - записать нужные таблицы в файл CSV, затем сжать и затем передать полученный файл в базу данных, где его можно распаковать и напрямую импортировать в базу данных. Это можно сделать для приведенной выше таблицы с помощью следующей команды SQL:

LOAD DATA
INFILE `location.csv`IGNORE
INTO TABLE Location
COLUMNS TERMINATED BY `,`
OPTIONALLY ENCLOSED BY '\"'  
LINES TERMINATED BY `\n`
(latitude, longitude)
SET id = NULL;

Этот процесс занял 15 минут, чтобы загрузить файл, 5 минут, чтобы сжать 2,2 ГБ файлов, 5 минут, чтобы распаковать файлы, и 2-3 минуты, чтобы создать файлы. Передача файла будет зависеть от возможностей вашей сети. При 30 минутах плюс время передачи по сети это был самый быстрый способ импорта больших объемов данных, которые мне были нужны, в базу данных, хотя это может потребовать больше работы с вашей стороны в зависимости от вашей ситуации.

Итак, есть 3 возможных решения этой проблемы, которые я обнаружил. Первый использует ту же структуру и позволяет легко понять и реализовать решение. Второй использует расширение структуры и позволяет осуществлять более крупные переводы за тот же период. Последний вариант, безусловно, самый быстрый и полезный, если объем данных является вопиющим, но для этого требуется работа с вашей стороны по созданию программного обеспечения.

0 голосов
/ 08 января 2019

4 стола? Я бы сделал 1 таблицу с 4 столбцами, , даже если исходные данные были бы не такими:

dt DATETIME  -- y/m/d:h 
lat SMALLINT
lng SMALLINT
amount ...
PRIMARY KEY (dt, lat, lng)

И я, вероятно, сделал бы всю работу непосредственно в SQL.

  1. LOAD DATA INFILE во все, что соответствует файлу (ам).
  2. Запустите несколько операторов SQL для преобразования в схему выше.
  3. Добавьте любые желаемые вторичные индексы в приведенную выше таблицу.

(В одном приложении я преобразовал часы в MEDIUMINT, который составляет всего 3 байта. Мне нужен был этот тип столбца в более чем 94M строках в нескольких таблицах.)

В лучшем случае ваш lid будет 3-байтовым MEDIUMINT с двумя 2-байтовыми SMALLINTs за ним. Дополнительная сложность, вероятно, перевешивает экономию всего лишь 94 МБ.

Общий размер: около 5 ГБ. Неплохо.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...