Обзор принципов движка хранения

Обзор MARS3

MARS3 — это проприетарный движок хранения на основе LSM-дерева, разработанный компанией YMatrix. Он использует архитектуру гибридного строчно-колоночного хранения. В дополнение к традиционной модели LSM, MARS3 внедряет двухэтапный путь записи «сначала строки, затем столбцы». Такой подход наследует преимущества строкового хранения для операций записи и сохраняет высокопроизводительные аналитические возможности колоночного хранения.

Ключевые особенности:

  • Сжатие с помощью цепочек кодирования (encoding chain).
  • Поддержка обновлений и удалений данных.
  • Управление многоверсионным параллелизмом (MVCC).
  • Индексация BRIN.
  • Гибридное строчно-колоночное хранение.

MARS3 разработан для удовлетворения требований как аналитической обработки (AP), так и транзакционной обработки (TP).

Движок поддерживает операции обновления и удаления через cláusулы UPDATE (за исключением режима Unique) и DELETE. Также поддерживаются добавление и удаление столбцов, операции COPY и pg_dump.

Внутренняя архитектура

Каждая таблица MARS3 внутри использует структуру LSM-дерева (Log-Structured Merge-Tree). LSM-дерево — это многоуровневая, упорядоченная дисковая структура данных. Её основная философия заключается в использовании производительности диска для пакетной последовательной записи, что значительно превосходит случайную запись.

Внутренняя архитектура MARS3

Run

Данные в MARS3 хранятся в упорядоченном виде. Непрерывный сегмент упорядоченных данных называется Run.

Существует два типа Run:

  • Rowstore Run: Оптимизирован для высокоскоростной ingestии. Входящие данные изначально хранятся в этом формате.
  • Columnstore Run: Оптимизирован для чтения и сжатия. Rowstore Run со временем конвертируются в этот формат.

Каждый Run имеет ограничение по размеру:

  • Параметр уровня таблицы max_runsize указывает максимальный размер одного Run при создании таблицы. Максимальное значение составляет 16384 МБ.
  • Значение по умолчанию — 4096 МБ.
  • Для просмотра файлов расширений и delta-файлов таблиц MARS3 можно использовать функцию matrixts_internal.mars3_files.
SELECT * FROM matrixts_internal.mars3_files('test');

Основные типы файлов:

  • DATA: Основной файл данных, хранящий пользовательские данные.
  • FSM (Free Space Map): В YMatrix обновления и удаления реализуются через MVCC, а не путем изменения на месте. Это создает «мертвые кортежи» (версии, невидимые для всех транзакций). FSM отслеживает это освобождаемое пространство для эффективного перераспределения.
  • LINK: Поддерживает отношения цепочки версий (восходящие/нисходящие) для кортежей во время уплотнения (compaction) в сценариях обновления/удаления.
  • DELTA: Хранит маркеры удаления (например, информацию XMAX). MARS3 не изменяет данные на месте; вместо этого используются DELTA-файлы и информация о версиях для скрытия старых данных и управления видимостью.
  • INDEX и INDEX_1_TOAST: Хранят структуры индексов. MARS3 в настоящее время поддерживает индексы BRIN и B-Tree.

Level

MARS3 организует данные по модели LSM-дерева. Файлы Run организованы в Уровни (Levels), от L0 до L9 (максимум 10 уровней).

Уплотнение (compaction) запускается, когда:

  • Количество Run на уровне превышает пороговое значение.
  • Общий размер Run на уровне превышает пороговое значение.

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

Структура уровней

Фоновые процессы слияния в YMatrix периодически обнаруживают состояние таблиц и выполняют уплотнение. Служебная функция matrixts_internal.mars3_level_stats предоставляет информацию о статусе каждого уровня таблицы MARS3:

SELECT * FROM matrixts_internal.mars3_level_stats('test') LIMIT 10;

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

Эмпирические правила здоровья:

  • Уровень 0: Нездоровое состояние, если количество Run > 3.
  • Уровень 1: Нездоровое состояние, если количество Run > 50.
  • Уровень > 1: Нездоровое состояние, если количество Run > 10.

Range и Stripe

Данные Columnstore записываются и читаются напрямую, без буферного слоя, такого как Shared Buffers или сброс страниц.

  • Range: Логическое окно строк. По умолчанию каждые compress_threshold (по умолчанию 1200) строк образуют Range.
  • Stripe: Физический блок данных для конкретного столбца в пределах Range (содержащий compress_threshold значений).

Если данные столбца внутри Stripe особенно велики, они разбиваются на фрагменты по 1 МБ. Чтение не обязательно выбирает весь объем compress_threshold за один раз.

RUN 
 └── Range (Разделение по строкам, по умолчанию 1200 строк на range)
      ├── Stripe столбца 1 (1200 данных)
      ├── Stripe столбца 2 (1200 данных)
      ├── Stripe столбца 3 (1200 данных)
      └── ...
  • Range: Логическое окно строк; внутри хранится в колоночном формате и сжимается по столбцам.
  • Stripe: Физический блок столбца; непрерывное хранилище для конкретного столбца в пределах диапазона из 1200 строк.
  • Datum: Минимальная единица значения (значение одной ячейки, нативное для ядра PostgreSQL).

Rowstore и Columnstore

MARS3 использует стратегию хранения «Сначала строки, затем столбцы». Данные поступают в систему в формате, оптимизированном для записи (rowstore), и постепенно конвертируются в формат, оптимизированный для анализа (columnstore), посредством фонового управления. Это обеспечивает непрерывную запись и бесперебойный анализ.

  • Rowstore: Оптимизирован для ingestии и доступа к свежим данным. Он быстро принимает новые данные и подходит для чтений небольшого масштаба или детального поиска, особенно сразу после вставки до начала уплотнения.
  • Columnstore: Оптимизирован для сканирования и агрегации. После организации columnstore значительно повышает пропускную способность сканирования и эффективность сжатия, делая крупномасштабные агрегации и запросы с фильтрацией более ресурсоэффективными.

Преимущества по сравнению с прямой колоночной записью:

  • Более быстрая ingestия для высокочастотной записи небольшими пакетами.
  • Сниженные требования к памяти для кэширования данных.
  • Обеспечение равномерного количества кортежей в блоках данных.

Ingestия данных

  • Данные вставляются в память через INSERT, затем записываются в Rowstore Run Уровня 0 (L0).
  • YMatrix поддерживает три режима записи, определяемых параметрами уровня таблицы prefer_load_mode и rowstore_size (см. Конфигурация):
    1. Normal: Режим по умолчанию. Новые данные записываются в Rowstore Run L0. После достижения rowstore_size данные сбрасываются в Columnstore Run L1. По сравнению с режимом Bulk, это требует одной дополнительной операции ввода-вывода, а конвертация из строк в столбцы происходит асинхронно. Подходит для высокочастотной записи небольшими пакетами при достаточной мощности ввода-вывода и высокой чувствительности к задержкам.
    2. Bulk: Режим пакетной загрузки. Данные записываются напрямую в Columnstore Run L1. По сравнению с режимом Normal, это сокращает одну операцию ввода-вывода, а конвертация происходит синхронно. Подходит для низкочастотной записи большими пакетами при ограниченной мощности ввода-вывода и низкой чувствительности к задержкам.
    3. Single: Данные вставляются напрямую в rowstore. Кортежи размещаются непосредственно в Shared Buffers.

Дополнительные сведения см. в разделе Путь записи.

Индексы MARS3

MARS3 в настоящее время поддерживает индексы BRIN и B-Tree.

  • B-Tree: Идеален для транзакционных систем, требующих «точного поиска». Использует указатели на уровне строк для быстрого позиционирования.
  • BRIN: Идеален для крупномасштабных аналитических систем, где преобладают «диапазонные сканирования». Использует сводки блоков для значительного сокращения ненужного ввода-вывода.

Примечание!
Одна таблица MARS3 поддерживает максимум 16 индексов (независимо от типа столбца или индекса). Таблицы MARS3 поддерживают только индексы mars3_brin и mars3_btree. CREATE INDEX CONCURRENTLY сейчас не поддерживается для таблиц MARS3.

Индексы BRIN

  • MARS3 поддерживает создание индексов mars3_brin и mars3_default_brin. Поддерживаются создание и удаление.
  • Каждый Run при создании генерирует свой собственный независимый файл индекса BRIN.
  • Default BRIN — это ключевая функция, обеспечивающая автоматическую поддержку BRIN на уровне таблицы без необходимости ручного создания. В отличие от стандартного CREATE INDEX USING BRIN (который приносит пользу только сканированию по индексу), Default BRIN также улучшает последовательное сканирование (Seq Scan), значительно повышая эффективность запросов. Важно отметить, что Default BRIN не занимает слот индекса. Даже при включенном Default BRIN можно создать до 16 дополнительных индексов.
Функция mars3_brin mars3_default_brin
Создание Ручное Автоматическое (ручные действия не требуются)
Поддержка запросов Фильтрация данных только при сканировании по индексу Фильтрация данных как при сканировании по индексу, так и при последовательном сканировании
Версия технологии brinV2 brinV2
Параметризованный запрос Поддерживается (param-IndexScan) Поддерживается (param-SeqScan)

Индексы B-Tree

B-Tree — это индекс общего назначения, основанный на сбалансированной многопутевой древовидной структуре. Он организует узлы индекса по значению ключа, обеспечивая быстрое и точное позиционирование отдельных строк или небольших диапазонов. Сложность запроса остается стабильной на уровне O(logN). Он эффективно поддерживает проверки на равенство, диапазонные сканирования и операции сортировки. Благодаря независимости от физического распределения данных, B-Tree обеспечивает исключительную стабильность в сценариях транзакций с высокой конкуренцией. Это выбор по умолчанию для первичных ключей, уникальных ограничений и запросов с высокой селективностью. Однако он не подходит для столбцов с низкой селективностью или широких диапазонных сканирований больших таблиц.

mars3btree — это специализированная реализация B-Tree для MARS3. Внутренние страницы остаются стандартными страницами B-Tree. Поддерживаются два типа:

  • NORMAL: Стандартный строково-ориентированный B-Tree (для RowStore), без сжатия.
  • COMPRESSED: Колоночно-ориентированный сжатый B-Tree (для ColumnStore), со сжатием.

Ключи сортировки

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

  • Правильно выбранные ключи сортировки создают сильную локальность данных внутри Run и на более высоких уровнях. Условия фильтрации запросов с большей вероятностью будут соответствовать непрерывным диапазонам, делая операции пропуска (skip-scans) более эффективными.
  • Неправильно выбранные ключи сортировки приводят к разрозненному распределению данных. Условия фильтрации не могут сузить диапазон сканирования, заставляя систему вести себя так, как будто выполняется полное сканирование таблицы, несмотря на наличие индексов.
  • MARS3 хранит данные в отсортированном порядке. Порядок сортировки должен быть указан через столбцы сортировки (допускается несколько столбцов) при создании таблицы. Эти поля составляют ключ сортировки.
  • Ключ сортировки можно указать только один раз. Его нельзя изменить, добавить к нему столбцы или удалить после создания.
  • Для максимизации выигрыша в производительности от упорядочивания выбирайте поля, часто используемые в фильтрах и обладающие высокой селективностью. Например, используйте временную метку события и ID устройства для таблиц мониторинга оборудования.
  • Если ключ сортировки текстовый и допустима сортировка по байтовому порядку, использование COLLATE C для этого столбца может ускорить сортировку.

Подробные принципы выбора см. в разделе Ключи сортировки и локальность данных.

Сжатие

Типизированный кодировщик

Начиная с версии 6.8.2 MARS3 по умолчанию использует типизированный кодировщик для сжатия данных. Если при создании таблицы не указан compresstype, используется именно этот способ. Также можно явно указать compresstype=auto, чтобы использовать типизированный кодировщик. Он динамически выбирает наиболее подходящий способ кодирования на основе типа столбца и характеристик каждого блока данных, без дополнительного вмешательства.

Параметр automode задает приоритет типизированного кодировщика:

  • automode=1: приоритет степени сжатия.
  • automode=2: приоритет скорости кодирования и декодирования. Значение по умолчанию: 2.

Типизированный кодировщик имеет следующие особенности:

  • Для распространенных типов данных он обеспечивает более высокий коэффициент сжатия, чем универсальные алгоритмы сжатия, такие как lz4 и zstd.
  • Он обеспечивает более высокую производительность кодирования и декодирования.

Сейчас типизированный кодировщик поддерживает только следующие типы:

  • smallint, integer, bigint
  • date, time, timestamp, timestamptz
  • text, varchar
  • numeric, только если для столбца задана фиксированная точность
  • float

Для остальных типов MARS3 использует zstd уровня 3.

О влиянии на производительность см. раздел Сжатие и производительность.

Обновления и удаления

  • Удаление: MARS3 записывает удаления в Delta-файл соответствующего Run. Физическое удаление данных происходит только во время уплотнения Run.
  • Обновление: MARS3 реализует обновления путем логического удаления исходных данных и вставки новой записи.
  • Режим Unique: Поддерживает DELETE. Для обновлений не требуется явная клаузула UPDATE; выполнение INSERT с тем же уникальным ключом автоматически выполняет обновление. Уникальный ключ соответствует ключу сортировки, определенному при создании.
    • Пример: CREATE TABLE mars3_t(c1 int NOT NULL, c2 int) USING MARS3 WITH (uniquemode=true) ORDER BY (c1, c2); Здесь уникальный ключ — (c1, c2).

Примечание!
Если включен режим Unique, первое поле в клаузуле ORDER BY должно быть определено с ограничением NOT NULL.

Технические подробности см. в разделе Обновления и удаления.

Уплотнение и освобождение места

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

Технические подробности см. в разделе Фоновое управление.

Поддержка MVCC

  • MVCC (Multiversion Concurrency Control), или многоверсионность, в основном обрабатывает обновления, модификации и удаления данных.
  • При многоверсионности обновления и удаления не изменяют данные на месте. Вместо этого создается новая версия, старая версия помечается как недействительная, и добавляются новые данные. Сохраняется несколько версий данных, каждая из которых несет информацию о версии.
  • MARS3 полагается на Delta-файлы и информацию о версиях для скрытия старых данных, управляя видимостью, а не изменяя данные на месте.
  • Примечание: Непрерывные обновления или удаления одного и того же Run увеличат физическое пространство, занимаемое его Delta-файлом. Однако рост прекратится, как только все данные в Run будут удалены. Уплотнение MARS3 автоматически очищает Dead (мертвые) данные. Кроме того, можно запланировать выполнение VACUUM для профилактической очистки Dead данных.

Поддержка сегментов (Buckets)

MARS3 Bucket — это механизм оптимизации параллельного выполнения на уровне хранилища, разработанный YMatrix для сценариев параллельного сканирования в архитектуре MPP. Организуя данные в несколько логических сегментов (buckets) на основе хеша ключа распределения на этапе записи, он гарантирует, что данные с одинаковым ключом распределения будут обрабатываться одним и тем же воркером во время параллельных сканирований. Это сохраняет семантику распределения данных (локус), позволяет избежать ненужного перераспределения данных (Motion) и обеспечивает скачок производительности от «более быстрого сканирования» к «более локальным вычислениям».

create table foo (c1 int, c2 int) using mars3 with (mars3options='nbuckets = 2');

Допустимые значения для nbuckets находятся в диапазоне от 1 до 128. Значение по умолчанию равно 1, что указывает на использование единого сегмента (т.е. сегментирование не выполняется). Включайте Bucket только для больших таблиц, которые выигрывают от параллельного сканирования. Bucket опирается на семантику hash-распределения, поэтому для таблиц без hash-распределения практической пользы нет. Unique Mode и continuous view не поддерживают режим Bucket. Изменение nbuckets является изменением уровня rewrite, поэтому значение лучше выбирать при создании таблицы.

Дополнительные технические сведения см. в разделе Технический обзор MARS3 Bucket.

Использование MARS3

Создание таблицы MARS3

При установленном расширении matrixts простейший способ создания таблицы — добавить клаузулы USING и ORDER BY в оператор CREATE TABLE. Расширенные примеры см. в разделе Лучшие практики проектирования таблиц.

CREATE TABLE metrics (
    ts              timestamp,
    dev_id          bigint,
    power           float,
    speed           float,
    message         text
) USING MARS3 
  ORDER BY (dev_id, ts);

Примечание!
Таблицы MARS3 поддерживают индексы BRIN, но они не являются обязательными.
Начиная с версии 6.3.0, требование указывать клаузулу ORDER BY при создании таблицы было удалено. Однако в production-среде рекомендуется явно проектировать ключ сортировки на основе основных условий фильтрации. Если включен Unique Mode, корректный ключ сортировки обязателен.

Параметры конфигурации

Примечание!
Параметры уровня таблицы MARS3 задаются в двух местах: во внешнем WITH (...) и во внутренней строке mars3options='a=1,b=2,...'. Эти категории нельзя смешивать. Например, compresstype, compress_threshold, automode и uniquemode относятся к внешним параметрам WITH, а rowstore_size, prefer_load_mode и nbuckets относятся к mars3options. Дополнительную информацию см. в разделе Параметры таблиц базы данных.

Распространенные внешние параметры WITH:

Параметр Единица По умолчанию Диапазон Описание
compresstype auto (6.8.2 и выше) none / auto / zstd / zlib / lz4 / rle_type / mxcustom Метод сжатия на уровне таблицы. auto использует типизированный кодировщик. mxcustom требует параметр encodechain.
compresslevel 0 0 – 19 Уровень сжатия. Фактический диапазон зависит от алгоритма; например, для zlib обычно используется 1 – 9.
compress_threshold Кортежи 1200 1 – 65535 Порог сжатия. Контролирует максимальное число кортежей, сжимаемых вместе для каждого столбца в одном Range. Слишком малое значение снижает эффект сжатия, слишком большое увеличивает потребление памяти.
encodechain Пусто Строка цепочки кодирования Используется только с compresstype=mxcustom для задания пользовательской цепочки кодирования.
automode 2 1 / 2 Стратегия для compresstype=auto. 1 отдает приоритет степени сжатия, 2 отдает приоритет скорости кодирования и декодирования.
object_size МБ 64 Положительное целое Размер объекта. Обычно не требует ручной настройки.
use_table_encoding 0 0 / 1 Использовать ли настройки кодирования уровня таблицы с более высоким приоритетом. Обычно оставляют значение по умолчанию.
uniquemode false true / false Включает Unique Mode. При включении ключ сортировки выступает уникальным ключом, а новые записи с тем же ключом заменяют старые версии.

Распространенные параметры mars3options:

Параметр Единица По умолчанию Диапазон Описание
prefer_load_mode normal normal / bulk / single Режим загрузки данных. normal — смешанная стратегия по умолчанию; bulk лучше подходит для редкой пакетной загрузки большого объема; single записывает напрямую в rowstore и подходит для небольших записей с требованием низкой задержки.
rowstore_size МБ 64 8 – 4096 Контролирует момент переключения L0 RowStore Run. Новый Run запускается, когда размер данных превышает это значение.
max_runsize МБ 4096 64 – 16384 Максимальный размер одного Run.
level_size_amplifier 8 1 – 1000 Коэффициент усиления размера уровня. Большее значение реже запускает compaction, но повышает риск усиления чтения; меньшее значение делает compaction более активным, но увеличивает фоновую нагрузку ввода-вывода.
default_brinkeys 30 -1 – максимальный номер атрибута Управляет столбцами, покрываемыми Default BRIN. 0 отключает его; -1 позволяет системе включить его для всех поддерживаемых сравнимых столбцов; N включает его для первых N сравнимых столбцов.
nbuckets 1 1 – 128 Количество бакетов. Используется для сохранения семантики распределения при параллельном сканировании. Рекомендации см. в разделе MARS3 Bucket Best Practices в документе «Лучшие практики проектирования таблиц и распределения данных».

Рекомендации по выбору prefer_load_mode:

Режим Подходящие сценарии Примечания
normal Режим по умолчанию для высокочастотных небольших пакетов и непрерывной записи. В конце записи MARS3 по размеру кэша решает, оставить данные в RowStore или сбросить их в ColumnStore, балансируя задержку и эффективность аналитики.
bulk Редкая пакетная загрузка большого объема, когда важнее сжатие ColumnStore и AP-запросы. Потребляет больше памяти; задержка одной операции записи обычно выше, чем у normal / single.
single Одиночные или небольшие записи с чувствительностью к задержке. Данные сначала попадают в RowStore, поэтому сжатие, BRIN и эффективность AP-запросов обычно ниже, чем у ColumnStore. После больших загрузок отслеживайте фоновый compaction или выполняйте VACUUM при необходимости.

Пример конфигурации:

CREATE EXTENSION matrixts;
CREATE TABLE t(
    time timestamp with time zone,
    tag_id int,
    i4 int4,
    i8 int8
)
USING MARS3
WITH (compresstype=auto, automode=2, compress_threshold=1200,
        mars3options='rowstore_size=64,prefer_load_mode=normal,level_size_amplifier=8,default_brinkeys=30,nbuckets=2')
DISTRIBUTED BY (tag_id)
PARTITION BY RANGE (time)
(
    START ('2026-02-01 00:00:00+08')
    END   ('2026-03-01 00:00:00+08')
    EVERY (INTERVAL '1 day')
)
ORDER BY (time, tag_id);

Служебные функции

  • matrixts_internal.mars3_level_stats: Проверяет статус каждого уровня в таблице MARS3. Полезно для оценки здоровья (например, прогресс слияния, количество Run).
  • matrixts_internal.mars3_files: Проверяет статус файлов таблицы MARS3 (файлы данных, Delta, индексы и т.д.).
  • matrixts_internal.mars3_info_brin: Проверяет статус конкретного индекса BRIN на таблице MARS3.

Обзор HEAP

HEAP — это движок хранения по умолчанию в YMatrix, унаследованный от PostgreSQL. Также известный как кучное хранение (heap storage), он поддерживает только строковое хранение и не поддерживает колоночное хранение или сжатие. Он основан на механизме MVCC и подходит для сценариев с частыми обновлениями и удалениями.

Механизм MVCC

В рамках MVCC таблицы HEAP не удаляют данные физически во время операций обновления или удаления. Вместо этого они полагаются на информацию о версиях для скрытия старых данных (управление видимостью). Следовательно, обширные обновления или удаления приводят к непрерывному росту физического пространства, занимаемого таблицей HEAP. Требуется регулярное выполнение VACUUM для освобождения места от старых данных.

Использование HEAP

Вы можете создать таблицу HEAP в YMatrix, используя следующий SQL:

CREATE TABLE disk_heap(
    time timestamp with time zone,
    tag_id int,
    read float,
    write float
)
DISTRIBUTED BY (tag_id);

Обзор AORO

AORO (Append-Only Row-Oriented) — это парадигма организации хранения, разработанная для аналитических баз данных. Данные записываются непрерывно в режиме «только добавление» (append-only) по строкам. Он не поддерживает обновления или удаления на месте. Версионирование поддерживается через временные метки или идентификаторы транзакций, балансируя пропускную способность записи, эффективность запросов и согласованность MVCC. AORO поддерживает строковое хранение.

Для AO-таблиц с частыми обновлениями и удалениями необходима регулярная очистка старых данных. Однако инструмент VACUUM для AO-таблиц должен сбрасывать битовые карты и сжимать физические файлы, что делает процесс обычно более медленным по сравнению с таблицами HEAP.

Примечание!
Подробную информацию, использование и лучшие практики regarding движков хранения см. в разделе Лучшие практики проектирования таблиц и распределения данных.