Фоновое управление

Стратегии уплотнения (Compaction) и сброса (Flush)

Прежде чем обсуждать уплотнение, необходимо понять три ключевых концепции:

  • Усиление чтения (Read Amplification): Фактический объем прочитанных данных превышает логический размер данных. Например, запрос к LSM-дереву может потребовать сканирования нескольких SSTables для получения одного ключа.
  • Усиление записи (Write Amplification): Фактический объем данных, записанных на диск, превышает логический размер данных. Например, запись одного ключа в LSM-дереве может запустить операцию уплотнения, вызывая перезапись значительно большего объема данных.
  • Усиление пространства (Space Amplification): Фактическое занимаемое дисковое пространство больше логического размера данных. Это часто происходит, когда старые недействительные версии данных остаются храниться в SSTables.

LSM-деревья обеспечивают высокую пропускную способность записи благодаря последовательной записи и фоновому уплотнению. Стратегия уплотнения определяет основной компромисс между производительностью записи, производительностью чтения и потреблением ресурсов. Традиционные LSM-деревья используют две основные стратегии уплотнения: Size-Tiered Compaction (послойное по размеру) и Level Compaction (уровневое).

  • Tiering (Послойное): Сначала накапливает файлы, затем объединяет их пакетами при достижении порога (оптимизировано для записи). Можно представить это как складывание файлов в ящик и их архивацию только при заполнении.
  • Leveling (Уровневое): Поддерживает каждый уровень отсортированным и организованным, предотвращая беспорядок (оптимизировано для чтения). Можно представить это как немедленное размещение каждого файла на четко категоризированной полке.

Image

Послойное уплотнение (Tiered Compaction, RocksDB)

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

  • Преимущества: Низкое усиление записи (слияния происходят более ситуативно; данные не переписываются многократно только для устранения перекрытий).
  • Недостатки: Высокое усиление чтения (запросы могут требовать сканирования нескольких Run).

Image

Стратегия послойного уплотнения по размеру (STCS) объединяет SSTables схожих размеров в новый файл. По мере сброса memtables на диск в виде SSTables, они начинаются как небольшие файлы. По мере роста количества небольших файлов и достижения порога, STCS уплотняет их в файлы среднего размера. Аналогично, когда количество файлов среднего размера достигает порога, они уплотняются в большие файлы. Этот рекурсивный процесс непрерывно генерирует все более крупные файлы.

Основные принципы:

  • Несколько SSTables с перекрывающимися диапазонами ключей допускаются в пределах одного уровня.
  • Файлы сначала накапливаются; единое слияние происходит только при достижении порога количества.

Пример: Предположим, что каждая SSTable содержит 4 ключа.

  1. Первый сброс: SST-A: [1 – 4]
  2. Второй сброс: SST-B: [5 – 8]
  3. Третий сброс (обновления + новые данные): Записываются ключи 3, 6, 9, 10. Генерируется SST-C: [3 – 10].

Структура Уровня 0: [1–4], [5–8], [3–10]

  • C перекрывается с A по ключам 3–4.
  • C перекрывается с B по ключам 5–8.

Запрос key=6 требует сканирования нескольких файлов, что иллюстрирует усиление чтения. При послойном уплотнении слияние запускается, когда количество файлов на уровне достигает порога, выдавая один большой файл (например, [1 – 10]). Таким образом, преимуществом является быстрая запись с низким усилением записи, но недостатком является то, что запросы должны сканировать несколько файлов, что приводит к усилению чтения.

Уровневое уплотнение (Leveling Compaction, Cassandra)

SSTables в пределах каждого уровня стремятся быть непересекающимися (непересекающиеся диапазоны ключей). Если возникает перекрытие, SSTable верхнего уровня и все перекрывающиеся SSTables нижнего уровня объединяются и перезаписываются.

  • Преимущества: Низкое усиление чтения (запрос ключа или диапазона обычно требует сканирования очень небольшого количества файлов).
  • Недостатки: Высокое усиление записи (частая перезапись больших объемов данных).

Image

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

Основные принципы:

  • Диапазоны ключей SSTable в пределах каждого уровня должны быть непересекающимися.
  • Вся система формирует непрерывное упорядоченное пространство.

Пример: Используя тот же сценарий, при обнаружении перекрытий ([1–4], [5–8], [3–10]) происходит немедленное слияние для получения [1–10]. Затем оно разделяется на [1–5] и [6–10] для обеспечения непересекающихся интервалов внутри уровня. Таким образом, преимуществом являются быстрые запросы (сканирование максимум одного файла), но недостатком является высокое усиление записи, непрерывное фоновое уплотнение и высокая нагрузка на ввод-вывод.

Измерение Tiered (Послойное) Leveling (Уровневое)
Перекрытие внутри уровня Допускается Не допускается
Стоимость записи Очень низкая Средняя-Высокая
Стоимость запроса Высокая Очень низкая
Усиление записи Низкое Высокое
Нагрузка на I/O Легкая Тяжелая
Стабильность задержки Плохая Хорошая
Основная цель Пропускная способность Время отклика

Реализация в MARS3

MARS3 использует стратегию послойного уплотнения (Tiered Compaction). В типичных рабочих нагрузках временных рядов (VIN + TS) непрерывная запись приводит к тому, что новые Run широко перекрываются с несколькими Run на нижних уровнях в пространстве ключей. Поддержание непересекающихся уровней с помощью уровневого уплотнения становится затруднительным, часто вызывая масштабные перезаписи. Это приводит к значительному усилению записи, которое еще больше усугубляется в сценариях MPP с множеством экземпляров.

Сценарий: Предположим, три устройства (VIN=A, B, C) непрерывно сообщают последние значения. В системе уже существует слой Уровня 1 (L1), где каждый файл охватывает определенный временной диапазон для VIN:

  • Существующий L1:
    • История A: A:[0 ~ 999], A:[1000 ~ 1999]
    • История B: B:[0 ~ 999], B:[1000 ~ 1999]
    • История C: C:[0 ~ 999], C:[1000 ~ 1999]

Поступление новых данных:

  • A сообщает: TS = 1500 ~ 1700
  • B сообщает: TS = 1600 ~ 1800
  • C сообщает: TS = 1400 ~ 1650

Эти новые записи формируют новый Run (например, в L0 или на верхнем уровне), называемый NewRun. NewRun содержит последние временные срезы для A, B и C, охватывая примерно:

  • A:[1500 ~ 1700]
  • B:[1600 ~ 1800]
  • C:[1400 ~ 1650]

Анализ перекрытий:

  • Новые данные A перекрываются с A:[1000 ~ 1999].
  • Новые данные B перекрываются с B:[1000 ~ 1999].
  • Новые данные C перекрываются с C:[1000 ~ 1999].

Следовательно, один NewRun перекрывается с несколькими файлами L1 (по одному на каждый VIN). Если одновременно пишут 10 000 VIN, один NewRun может перекрываться с сотнями или тысячами файлов L1. Уровневое уплотнение требует, чтобы файлы L1 были непересекающимися. Поэтому слияние NewRun в L1 потребует чтения NewRun плюс всех перекрывающихся файлов L1, их повторной сортировки и записи новых файлов L1 для сохранения свойства непересекаемости. Даже если новая запись мала (например, 1 ГБ), если она затрагивает множество исторических сегментов, она вовлекает огромные объемы исторических данных (например, 10 ГБ, 50 ГБ или 100 ГБ) для перезаписи, вызывая сильное усиление записи.

Напротив, послойное уплотнение по размеру (Size-Tiered Compaction) не требует непересекающихся нижних уровней. Оно в основном объединяет Run схожих размеров. Оно не вовлекает大量 (большое количество) файлов нижнего уровня исключительно для устранения перекрытий. Таким образом, в этом сценарии усиление записи значительно снижается.

Кроме того, Run в MARS3 представляют собой колоночные структуры, требующие достаточной физической непрерывности для обеспечения пропускной способности сканирования и эффективности сжатия. Это конфликтует с мелкой гранулярностью SSTables, часто используемой в уровневом уплотнении. Послойное уплотнение по размеру лучше подходит для этой рабочей нагрузки: оно объединяет Run одинакового размера, значительно снижая усиление записи. Кроме того, при потоках данных, прогрессирующих во времени, оно естественным образом формирует организацию Run, кластеризованную по времени, делая фильтрацию по времени и пропуск сканирования (skip-scans) более эффективными.

Для контроля усиления чтения MARS3 ограничивает коэффициент усиления чтения на каждом уровне. Как только этот коэффициент превышен, запускается уплотнение на нижний уровень.

Image

Параметр level_size_amplifier задает коэффициент усиления для размеров уровней. Порог для запуска слияния на конкретном уровне рассчитывается как: rowstore_size * (level_size_amplifier ^ (level - 1)).

  • Большее значение приводит к более медленному чтению, но более быстрой записи.
  • Значение должно определяться на основе конкретных характеристик рабочей нагрузки (преобладание записи или чтения, коэффициенты сжатия и т.д.).
  • Внимание: Убедитесь, что количество Run на уровень не станет чрезмерным, так как это ухудшит производительность запросов и может даже заблокировать вставку новых данных.

По сути, level_size_amplifier контролирует, насколько каждый уровень может быть больше предыдущего. Верхние уровни действуют как буферы, а нижние уровни служат долгосрочным хранилищем.

  • Больший усилитель: Уровни могут накапливать данные дольше, приводя к большему количеству Run. Это увеличивает количество объектов, проверяемых во время запросов (более высокое усиление чтения), но снижает частоту уплотнения (более быстрая запись).
  • Меньший усилитель: Запускает опускание и организацию раньше, resulting в меньшем количестве более чистых Run. Это улучшает скорость чтения, но увеличивает частоту уплотнения.

Вместе rowstore_size и level_size_amplifier создают контролируемый ритм опускания данных. Это ограничивает накопление Run в модели Tiered, ограничивая усиление чтения при сохранении преимуществ пропускной способности записи. Подробнее см. раздел Наблюдаемость.

Планировщик уплотнения (Compaction Scheduler)

Image

  1. Планировщик уплотнения (Compaction Scheduler): Постоянный вспомогательный процесс, запускаемый при старте базы данных. Когда rowstore достигает rowstore_size, система переключается на новый rowstore. Сигнал отправляется процессу планировщика через общую память, содержащий детали запроса на уплотнение (ID отношения и уровень). Планировщик отвечает за регистрацию новых воркеров для выполнения уплотнения.

    • Обязанности:
      • Запуск и остановка воркеров уплотнения.
      • Управление и повторное использование воркеров уплотнения.
      • Определение приоритетов задач уплотнения.
      • Корректный ответ на запросы остановки базы данных.
  2. Воркер уплотнения (Compaction Worker): Выполняет конкретные запросы на уплотнение. Если усиление чтения уровня превышает лимит, воркер уведомляет планировщик о новом запросе на уплотнение. Из-за текущих ограничений модели процессов максимальное количество воркеров уплотнения жестко закодировано и равно 16.

  3. Зонд уплотнения (Compaction Prober): Активно опрашивает наличие задач уплотнения, таких как:

    • Запланированные задачи уплотнения.
    • Прерванные задачи уплотнения.
    • Рабочий процесс:
      1. Backend вставки непрерывно пишет в RowStore.
      2. Когда RowStore достигает порога, он переключается на новый RowStore.
      3. Отправляет SIGUSR1 планировщику и записывает информацию о запросе на уплотнение (relid, level) в общую память.
      4. Планировщик просыпается, считывает запрос из общей памяти:
        • Ставит запросы в очередь, дедуплицирует и приоритезирует их.
        • Выбирает нового воркера или повторно использует простаивающий.
      5. Воркер начинает выполнение уплотнения.

Приоритет уплотнения

Когда несколько таблиц требуют уплотнения, но доступно только 16 воркеров, механизм приоритетов гарантирует, что «наиболее нуждающиеся» таблицы будут уплотнены своевременно. Факторы приоритета включают:

  • Иерархия уровней: Нижние уровни имеют более высокий приоритет.
  • Активное против пассивного (Eager vs. Lazy): Ручные против автоматических триггеров.
  • Тип уплотнения.

Автозондирование (Autoprobe)

Autoprobe периодически сканирует все таблицы MARS3, вычисляя оценку на основе возраста транзакции, деленного на размер (age / size). Он выбирает top N уровней с наибольшими оценками для запуска задач сброса/уплотнения, эффективно сжимая исторические уровни. Функция matrixts_internal.mars3_autoprobe_candidates() отображает уровни-кандидаты и их оценки:

adw=# SELECT * FROM matrixts_internal.mars3_autoprobe_candidates();
 segid | datname |   relname   | level | nruns |    age     |   bytes   |         score
-------+---------+-------------+-------+-------+------------+-----------+-----------------------
     0 | adw     | t_w0_nosort |     1 |    13 |     616005 |  49888440 |  0.012347650076851471
     0 | adw     | t_w0_nosort |     0 |     1 | 2147483647 |  25034752 |     85.78010467209741
     0 | adw     | t_w3b_3key  |     0 |     1 | 2147483647 |  25034752 |     85.78010467209741
     0 | adw     | t_w1_1key   |     1 |     7 |     329693 |  23821378 |  0.013840215288972788
...

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

  • mars3_autoprobe_blacklist(): Просмотр черного списка.
  • mars3_autoprobe_blacklist_remove(regclass, level): Удаление конкретного уровня из черного списка для текущей базы данных.
  • mars3_autoprobe_blacklist_clear(): Очистка всего черного списка.

Параметры GUC:

  1. mars3.autoprobe_period: Контролирует интервал зондирования в секундах. Значение > 0 включает его; 0 отключает (по умолчанию). Начните с 10 минут и настройте в зависимости от рабочей нагрузки.
  2. mars3.autoprobe_workers: Контролирует, сколько уровней выбирается за одно зондирование (по умолчанию: 2). Примечание: Они занимают стандартные слоты воркеров уплотнения; новые воркеры не создаются.
  3. mars3.autoprobe_retry: Контролирует попытки повторения. Значение 2 означает всего 3 попытки.
  4. mars3.autoprobe_blacklist_size: Контролирует максимальный размер черного списка.

Влияние отключения Autoprobe: Таблицы без новых записей не будут подвергаться уплотнению. Таблицы с активными записями продолжат слияние в обычном режиме.

Наблюдаемость

Проверьте статус воркеров:

ps aux | grep postgres | grep 'compact worker'

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

Журналы базы данных также содержат информацию об активности уплотнения. Создайте таблицу t1 и непрерывно вставляйте данные:

postgres=# CREATE TABLE t1(id int, info text)
USING mars3 WITH(mars3options='prefer_load_mode=single,rowstore_size=64');
NOTICE:  Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'id' as the Greenplum Database data distribution key for this table.
HINT:  The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew.
CREATE TABLE

Откройте новое окно и непрерывно мониторьте состояния уровней с помощью matrixts_internal.mars3_level_stats:

Image

Наблюдение: После заполнения L0 до 64 МБ (rowstore_size), L1 кратко показывает 32 КБ, и данные записываются напрямую в L2. Последующие записи также идут напрямую в L2. Почему не сначала в L1?

Image

MARS3 включает логику «Корректировки уровня» (GetDesiredLevel):

  • Вычисляет целевой уровень (desired_level) для Run на основе его TotalSize.
  • Если текущий уровень (cur_level) отличается, он блокирует Run и перемещает его на целевой уровень.

Поток вычислений:

  • run_sizerowstore_size = 64 МБ.
  • total_amp = run_size / rowstore_size = 64 МБ / 64 МБ = 1.0.
  • desired_level = log(1.0) / log(8) = 0.

Теоретический относительный уровень равен 0.

if (desired_level < 0) {
    ret = desired_level < -0.5 ? 1 : 2;
} else {
    ret = Min(2 + round(desired_level), MAXLEVELS - 1);
}

Поскольку desired_level = 0, выполняется ветвь else:

  • round(0) = 0.
  • ret = 2 + 0 = 2 (с учетом защиты верхнего предела Min).

Результат: GetDesiredLevel() возвращает 2. Согласно новым правилам, Run размером 64 МБ принадлежит L2.

Принципы проектирования

В модели Tiered MARS3 стабильный геометрический рост достигается не путем строгого мгновенного обеспечения общего размера на уровень, а путем позволяя уплотнению постепенно выравнивать целевые размеры Run с коэффициентом усилителя. Основные пороги для запуска уплотнения:

  • Старая версия (< REL_V1): threshold(level) = rowstore_size × amp^level
  • Новая версия (>= REL_V1): threshold(level) = rowstore_size × amp^(level - 1)

Здесь bytes_threshold оценивает размер входного набора для операции уплотнения/выбора (pick). Уплотнение выбирает N Run, чей total_size приближается к bytes_threshold. Рассчитанные пороги (предполагая rowstore_size=64МБ, amp=8):

  • Уровень 1: 64 МБ
  • Уровень 2: 512 МБ
  • Уровень 3: 4 ГБ
  • Уровень 4: 32 ГБ
  • ...

Применяется ограничение Min(MAX_RUN_SIZE, ...). Если рассчитанный размер превышает MAX_RUN_SIZE, предел ограничивается, чтобы предотвратить чрезмерно большие задачи или выходные Run.

Мотивация проектирования

  1. Мотивация А: Стабилизация модели геометрического роста.

    • rowstore_size представляет гранулярность rowstore.
    • L1+ хранит сжатые колоночные данные, где размеры в байтах значительно варьируются.
    • Без смещения базовой линии жесткое применение одной и той же формулы могло бы вызвать искажения: L1 сжимается из-за сжатия → запускает erratic ритмы уплотнения → приводит к неконтролируемому усилению чтения или накоплению.
    • Новая версия использует level-1 в формуле и добавляет +2 в классификации Run, чтобы выровнять базовую линию модели на более стабильные слои. Инженерный принцип: Не используйте самый нестабильный слой в качестве линейки.
  2. Мотивация Б: Экспоненциальное масштабирование задач с верхними пределами.

    • Экспоненциальный рост: Более глубокие уровни должны обрабатывать большие объемы данных за одно слияние, чтобы избежать частых возмущений глубоких данных (что вызывает усиление записи и дрожание I/O).
    • MAX_RUN_SIZE: Предотвращает становление любой отдельной задачи уплотнения или выходного Run слишком большими и голодание системы. Это настраивается на уровне таблицы.
  3. Мотивация В: Настраиваемый контроль усиления чтения.

    • Больший level_size_amplifier означает более быстрый рост порога → менее частое уплотнение → более плавная запись.
    • Однако это позволяет накапливаться большему количеству Run → выше риск усиления чтения.
    • Следовательно, больший усилитель приводит к более медленному чтению, но более быстрой записи.

Проверка

Используя пример таблицы t1, наблюдайте, когда L2 запускает «Выбор» (Pick). Согласно модели расчета, L2 должен запускать уплотнение около 512 МБ.

                                    Thu 29 Jan 2026 02:42:07 PM CST (каждую 1с)

 segid | level | total_nruns | visible_nruns | invisible_nruns | object_nruns | object_visible_nruns | level_size
-------+-------+-------------+---------------+-----------------+--------------+----------------------+------------
     3 |     0 |           1 |             1 |               0 |            0 |                    0 | 64 MB
     3 |     1 |           0 |             0 |               0 |            0 |                    0 | 0 bytes
     3 |     2 |          15 |            15 |               0 |            0 |                    0 | 533 MB
     3 |     3 |           0 |             0 |               0 |            0 |                    0 | 0 bytes
(4 строки)

                                    Thu 29 Jan 2026 02:42:08 PM CST (каждую 1с)

 segid | level | total_nruns | visible_nruns | invisible_nruns | object_nruns | object_visible_nruns | level_size
-------+-------+-------------+---------------+-----------------+--------------+----------------------+------------
     3 |     0 |           2 |             2 |               0 |            0 |                    0 | 69 MB
     3 |     1 |           0 |             0 |               0 |            0 |                    0 | 0 bytes
     3 |     2 |          15 |            15 |               0 |            0 |                    0 | 533 MB
     3 |     3 |           0 |             0 |               0 |            0 |                    0 | 0 bytes
(4 строки)

                                    Thu 29 Jan 2026 02:42:09 PM CST (каждую 1с)

 segid | level | total_nruns | visible_nruns | invisible_nruns | object_nruns | object_visible_nruns | level_size
-------+-------+-------------+---------------+-----------------+--------------+----------------------+------------
     3 |     0 |           1 |             1 |               0 |            0 |                    0 | 16 MB
     3 |     1 |           0 |             0 |               0 |            0 |                    0 | 0 bytes
     3 |     2 |          16 |            16 |               0 |            0 |                    0 | 577 MB
     3 |     3 |           1 |             0 |               1 |            0 |                    0 | 32 kB
(4 строки)

                                    Thu 29 Jan 2026 02:42:10 PM CST (каждую 1с)

 segid | level | total_nruns | visible_nruns | invisible_nruns | object_nruns | object_visible_nruns | level_size
-------+-------+-------------+---------------+-----------------+--------------+----------------------+------------
     3 |     0 |           1 |             1 |               0 |            0 |                    0 | 16 MB
     3 |     1 |           0 |             0 |               0 |            0 |                    0 | 0 bytes
     3 |     2 |          16 |            16 |               0 |            0 |                    0 | 626 MB
     3 |     3 |           1 |             0 |               1 |            0 |                    0 | 32 kB
(4 строки)

...
...

                                    Thu 29 Jan 2026 02:42:26 PM CST (каждую 1с)

 segid | level | total_nruns | visible_nruns | invisible_nruns | object_nruns | object_visible_nruns | level_size
-------+-------+-------------+---------------+-----------------+--------------+----------------------+------------
     3 |     0 |           1 |             1 |               0 |            0 |                    0 | 32 MB
     3 |     1 |           0 |             0 |               0 |            0 |                    0 | 0 bytes
     3 |     2 |          16 |            16 |               0 |            0 |                    0 | 626 MB
     3 |     3 |           1 |             0 |               1 |            0 |                    0 | 467 MB
(4 строки)

...
...

                                    Thu 29 Jan 2026 02:43:58 PM CST (каждую 1с)

 segid | level | total_nruns | visible_nruns | invisible_nruns | object_nruns | object_visible_nruns | level_size
-------+-------+-------------+---------------+-----------------+--------------+----------------------+------------
     3 |     0 |           1 |             1 |               0 |            0 |                    0 | 32 MB
     3 |     1 |           0 |             0 |               0 |            0 |                    0 | 0 bytes
     3 |     2 |           1 |             1 |               0 |            0 |                    0 | 36 MB
     3 |     3 |           1 |             1 |               0 |            0 |                    0 | 531 MB
(4 строки)

                                    Thu 29 Jan 2026 02:43:59 PM CST (каждую 1с)

 segid | level | total_nruns | visible_nruns | invisible_nruns | object_nruns | object_visible_nruns | level_size
-------+-------+-------------+---------------+-----------------+--------------+----------------------+------------
     3 |     0 |           1 |             1 |               0 |            0 |                    0 | 32 MB
     3 |     1 |           0 |             0 |               0 |            0 |                    0 | 0 bytes
     3 |     2 |           1 |             1 |               0 |            0 |                    0 | 36 MB
     3 |     3 |           1 |             1 |               0 |            0 |                    0 | 531 MB
(4 строки)

Анализ журналов и явлений:

14:42:07 ~ 14:42:08

  1. L0 RowStore достигает точки переключения для нового Run.
  2. L2 накопил 15 колоночных Run, всего 533 МБ.
    • L0: 1 Run, 64 МБ
    • L2: 15 Run, 533 МБ
    • L1: 0
    • L3: 0

14:42:09 ~ 14:42:10

  1. L0 падает с 69 МБ до 16 МБ: Указывает на произошедший сброс/переключение; начинается накопление нового Run.
  2. Количество Run в L2 увеличивается с 15 до 16: Newly сброшенный колоночный Run помещается в L2.
  3. L3 показывает небольшой (32 КБ), невидимый Run: Вероятно, заполнитель, метаданные или временный артефакт, обычный во время ongoing уплотнения.
    • L0: Возврат к 16 МБ (накопление нового Run).
    • L2: 16 Run, 577 → 626 МБ (добавлен новый Run).
    • L3: 1 Run, невидимый=1, 32 КБ.

14:42:26

  1. Уплотнение L2 все еще выполняется.
  2. Выходной Run L3 значительно вырос, но остается невидимым, указывая на то, что выходной Run еще не зафиксирован или ожидает обновления метаданных.
    • L2: 16 Run, 626 МБ.
    • L3: Невидимый Run растет с 32 КБ → 467 МБ.

14:43:58 ~ 14:43:59 (Завершение)

  • L2: 1 Run, 36 МБ.
  • L3: 1 Run, 531 МБ (Видимый).
  • L0: 32 МБ (Накопление нового Run RowStore).

Вывод: Уплотнение завершено и зафиксировано.

  • 16 Run в L2 были в основном объединены, остался только один маленький Run (36 МБ). Обычно это Run, сгенерированный во время уплотнения, который не был выбран для текущего слияния, или остаток из-за границ порогов.
  • L3 теперь содержит один большой видимый Run (531 МБ), который является результатом уплотнения L2→L3.
  • L2 продолжает запись, указывая на стабильную работу.

Image

Примечание: bytes_threshold для L0 равен 0, так как он сбрасывается напрямую; эта метрика менее значима для L0. RunLife-Flushed используется для отладки кода для отслеживания жизненного цикла Run:

  • RunLife-Created: RUN создан.
  • RunLife-Flushed: RUN прочитан и сброшен.
  • RunLife-Recycled: RUN переработан и использован повторно.

Image

Вернуться к предыдущему разделу: Принципы движка хранения