# Параллельное слияние чанков в Manticore Search

Manticore Search теперь поддерживает параллельное слияние дисковых RT-чанков и N-сторонние слияния, что заметно сокращает время компактизации без просадки пропускной способности загрузки.

Начиная с **Manticore Search 24.4.0**, компактизация RT-таблиц использует более эффективную модель выполнения. Вместо последовательного слияния пар чанков здесь появилось два важных улучшения:

- слияния дисковых чанков могут выполняться параллельно
- каждая задача слияния может объединять более двух чанков одновременно

- [parallel_chunk_merges](https://manual.manticoresearch.com/Server_settings/Searchd#parallel_chunk_merges): сколько задач слияния дисковых чанков RT может выполняться одновременно
- [merge_chunks_per_job](https://manual.manticoresearch.com/Server_settings/Searchd#merge_chunks_per_job): сколько дисковых чанков RT может объединять одна задача за один проход

Документация по компактизации тоже обновлена: теперь описывается команда **OPTIMIZE** как **N-стороннее слияние**, которым занимается **пул фоновых воркеров**, а не один последовательный тред слияния.

## Почему это важно

Для RT-нагрузок важна не только скорость вставки документов, но и то, сколько времени требуется компактизации, чтобы догнать поток данных и вернуть таблицу к целевому количеству чанков.

Это особенно заметно, когда:

- вы загружаете данные с постоянной скоростью
- `optimize_cutoff` установлен достаточно низко, чтобы слияния начинались рано
- вы ждёте завершения компактизации, прежде чем считать загрузку полностью завершённой

Это имеет наибольшее значение в двух типичных случаях:

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

Таблица становится доступной для поиска ещё до завершения компактизации, но «полностью доступна для поиска» и «полностью оптимизирована» — не одно и то же. Повышенное число чанков всё равно имеет значение, если вам важно держать таблицу ближе к целевому состоянию, ограничивать объём фоновых слияний до следующей волны загрузки или сокращать окно, в котором хранилище занято постзагрузочной компактизацией.

Чтобы продемонстрировать разницу, мы загрузили **10 миллионов документов** в RT‑таблицу. Каждый документ содержит:

- `id bigint`
- `name text` с сгенерированным текстом от 10 до 100 слов
- `type int`

Таблица была создана с помощью:

```sql
CREATE TABLE test(id bigint, name text, type int) optimize_cutoff='16'
```

То есть цель состояла в том, чтобы скомпактировать таблицу примерно до 16 дисковых чанков.

Для бенчмарка мы использовали [Manticore Load Emulator](/blog/manticore-load/) — наш инструмент для генерации нагрузки и бенчмарков. Он удобен для воспроизведения таких сценариев, стресс-тестирования загрузки и сравнения конфигурационных изменений без необходимости каждый раз писать собственные скрипты.

Данные были загружены с помощью:

```bash
manticore-load \
  --cache-gen-workers=5 \
  --drop \
  --batch-size=1000 \
  --threads=5 \
  --total=10000000 \
  --init="CREATE TABLE test(id bigint, name text, type int) optimize_cutoff='16'" \
  --load="INSERT INTO test(id,name,type) VALUES(<increment>,'<text/10/100>',<int/1/100>)" \
  --wait
```

## Раньше: одна задача слияния, по два чанка за раз

Если принудительно вернуть старое поведение:

```bash
mysql -P9306 -h0 -e "set global parallel_chunk_merges=1; set global merge_chunks_per_job=2"
```

Запуск выглядел так:

- слияние началось через **14 секунд**, когда было вставлено около **1,8M** документов
- все **10M** документов были загружены за **1 минуту 18 секунд**
- к этому моменту данные уже были полностью доступны для поиска
- компактизация продолжала работать в фоне до **3 минут 23 секунд**

На `01:18` таблица всё ещё имела более 50 чанков. Ближе к концу загрузки статус выглядел так:

```text
17:14:50  01:17     98%         133      128.4K   21%     5          53        1         4.22GB      9.9M
17:14:51  01:18     100%        131      310.9K   15%     1          53        1         4.27GB      10.0M
...
17:16:55  03:22     100%        0        49.4K    4%      1          17        1         4.27GB      10.0M
...
Total time:       03:23
```

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

## Теперь: параллельные слияния и больше чанков за одну джобу

С новыми настройками:

```bash
mysql -P9306 -h0 -e "set global parallel_chunk_merges=3; set global merge_chunks_per_job=5"
```

Тот же сценарий завершился заметно быстрее:

- слияние снова началось примерно через **14 секунд**
- все **10M** документов снова были загружены примерно за **1 минуту 18 секунд**
- полная компактизация завершилась всего за **1 минуту 31 секунду**

Конец прогона выглядел так:

```text
17:19:22  01:17     99%         127      127.9K   28%     6          26        1         4.22GB      9.9M
17:19:23  01:18     100%        132      1883.8K  17%     1          23        1         4.25GB      10.0M
...
17:19:36  01:31     100%        0        110.2K   3%      1          17        1         4.25GB      10.0M
...
Total time:       01:31
```

## Что изменилось на практике

Сама фаза загрузки почти не изменилась:

- старые настройки: **1:18** для загрузки всех данных
- новые настройки: **1:18** для загрузки всех данных

Основной выигрыш пришёлся на компактизацию после загрузки:

- старые настройки: около **2:05** дополнительного времени слияния после завершения загрузки
- новые настройки: около **0:13** дополнительного времени слияния после завершения загрузки

Это примерно:

- **на 55 % меньше общее время**, с **3:23** до **1:31**
- примерно **на 90 % меньше хвост слияний** после вставки последнего документа

Число чанков во время загрузки тоже было заметно ниже. Ближе к концу загрузки:

- старые настройки: **53 чанка**
- новые настройки: **23 чанка**

То есть улучшение не сводится только к более раннему завершению компактизации. Оно ещё и заметно лучше удерживает количество чанков под контролем, пока данные продолжают вставляться.

## Что насчёт новых дефолтов?

На этом же сервере, с новыми дефолтами и вообще без явного тюнинга, тот же сценарий завершился за:

```text
Total time:       01:57
```

Это уже заметно лучше старого результата `03:23`, при этом остаётся пространство для дополнительной настройки с помощью:

- `parallel_chunk_merges`
- `merge_chunks_per_job`

Иными словами, новые дефолты уже улучшают поведение «из коробки», а системы с достаточным запасом по I/O могут ещё сильнее ускорить компактизацию, если аккуратно увеличивать оба параметра.

## Результаты бенчмарков шире: строчное и колоночное хранение

Пример с 10 млн документов выше хорошо показывает механику процесса, но общая картина ещё интереснее. В более широкой матрице тестов мы измерили суммарное время **загрузка + оптимизация** как для строчного, так и для колоночного хранения при разных значениях:

- `parallel_chunk_merges`
- `merge_chunks_per_job`

Главный вывод, который мы сделали, — в том, что в некоторых случаях настройка этих параметров может сократить суммарное время загрузки + оптимизации:

- до **4.6x** для **строчного** хранения
- до **6.8x** для **колоночного** хранения

Ниже — картина «лучшее против худшего» из этого набора тестов:

| Хранилище | Лучшие настройки | Лучшее время | Самые медленные настройки | Самое медленное время | Улучшение |
| --- | --- | --- | --- | --- | --- |
| Строчное | `parallel_chunk_merges=4`, `merge_chunks_per_job=5` | 14:35 | `parallel_chunk_merges=1`, `merge_chunks_per_job=2` | 67:15 | 4.61x |
| Колоночное | `parallel_chunk_merges=4`, `merge_chunks_per_job=5` | 15:10 | `parallel_chunk_merges=1`, `merge_chunks_per_job=2` | 99:14 | 6.80x |

В полных результатах также просматривается полезный паттерн тюнинга:

- лучшие запуски для обоих режимов хранения группируются вокруг `parallel_chunk_merges=4..5`
- лучшие запуски также группируются вокруг `merge_chunks_per_job=4..5`
- самые медленные результаты постоянно были при `parallel_chunk_merges=1` и `merge_chunks_per_job=2`

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

## Что меняют эти две настройки

В новой документации есть две отдельные настройки:

- `parallel_chunk_merges` увеличивает количество задач слияния, которые могут выполняться одновременно
- `merge_chunks_per_job` увеличивает количество чанков, которые может обрабатывать каждая задача

Низкие значения `merge_chunks_per_job` упрощают запуск большего числа задач параллельно, потому что каждая задача забирает меньше чанков из доступного пула. Если в таблице много чанков, ожидающих компактизации, более мелкие задачи оставляют больше независимых чанков для других воркеров, поэтому планировщик может одновременно держать активными несколько слияний. Более высокие значения уменьшают общее число шагов слияния, но каждая задача становится тяжелее и забирает большую долю доступных чанков, из-за чего места для параллельных задач остаётся меньше.

Правильный баланс зависит от вашего хранилища и нагрузки, но приведённый выше бенчмарк показывает, что сочетание обоих подходов может заметно сократить время ожидания завершения компактизации RT-чанков.

## Вывод

Если из-за RT-нагрузок вам приходится слишком долго ждать завершения компактизации чанков после массовых вставок, новая модель параллельного слияния заметно меняет ситуацию.

В этом тесте с 10 млн документов и параметром `optimize_cutoff=16`:

| Режим | Доступен для поиска в | Полностью оптимизирован в |
| --- | --- | --- |
| Старые настройки: `parallel_chunk_merges=1`, `merge_chunks_per_job=2` | 1:18 | 3:23 |
| Новые дефолты | 1:18 | 1:57 |
| Настроенные новые параметры: `parallel_chunk_merges=3`, `merge_chunks_per_job=5` | 1:18 | 1:31 |

- время до момента, когда все данные стали доступны для поиска, осталось прежним
- время до завершения сжатия чанков сократилось с **3:23** до **1:31**
- даже новые дефолты сократили общее время до **1:57**

Это именно тот тип улучшения, который важен для рабочих RT-нагрузок. Данные становятся доступны для поиска сразу после загрузки, и этот момент в обоих прогонах оставался примерно одинаковым. Разница проявляется позже: насколько долго сервер продолжает тратить ресурсы на фоновую компактизацию чанков, прежде чем таблица вернётся к своей целевой форме. Если ваш рабочий процесс зависит от того, что таблица снова станет компактной до следующей крупной загрузки, до закрытия окна работ или до переключения системы на поисковую нагрузку, которая должна работать с меньшим количеством чанков и меньшей фоновой нагрузкой от слияний, то это улучшение действительно существенно.
