Обзор принципов хранилища


1 Обзор MARS3

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

MARS3 поддерживает обновление и удаление данных с помощью ключевых слов UPDATE и DELETE (за исключением режима Unique).

MARS3 поддерживает добавление и удаление столбцов, а также операции COPY и pg_dump.

1.1 Внутренние принципы

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

Ниже приведена схема внутренней структуры MARS3:

Рассмотрим эту схему по уровням.

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

  • В MARS3 данные хранятся в отсортированном порядке. При создании таблицы необходимо указать один или несколько столбцов сортировки для определения порядка. Эти столбцы называются ключом сортировки.
  • Ключ сортировки может быть определён только один раз. Его нельзя изменять, добавлять или удалять после создания.
  • Для максимального использования преимуществ упорядочивания данных выбирайте в качестве ключа сортировки часто запрашиваемые столбцы с высокой селективностью фильтрации. Например, в таблице мониторинга устройств используйте временну́ю метку события и идентификатор устройства в качестве ключа сортировки.
  • Если ключ сортировки имеет текстовый тип и допустимо байтовое упорядочивание, применение COLLATE C к этому столбцу ускорит сортировку.
  • Ключевое слово SQL для указания ключа сортировки: ORDER BY.

1.1.2 Rowstore и Columnstore

  • MARS3 поддерживает режим хранения строки-затем-столбцы или прямое столбцовое хранение. Режим загрузки задаётся параметром prefer_load_mode. Подробности см. в разделе Настройка ниже.
  • В гибридном режиме входящие данные сначала хранятся в формате строк. После накопления достаточного объёма они преобразуются в столбцовый формат.
  • По сравнению с прямой столбцовой загрузкой этот подход имеет ряд преимуществ:
    • Более высокая скорость записи при частых вставках малыми пакетами
    • Меньшее потребление памяти для буферизации
    • Однородное количество кортежей в каждом блоке данных

1.1.3 Run, файлы Delta и метаданные

  • На основе ключа сортировки данные в MARS3 хранятся в отсортированном порядке. Непрерывный сегмент отсортированных данных называется Run.
  • Runs бывают двух типов: для быстрого ввода данных вставленные данные хранятся в виде Run в формате строк; позже, для эффективного чтения и сжатия, Run в формате строк преобразуются в Run в столбцовом формате.
  • У каждого Run есть собственный набор файлов Delta. Помимо основных данных (файл Data), существуют Toast-файлы для больших значений, Delta-файлы для записей удалений, индексные файлы для индексации и Link-файлы для отслеживания слияний (типы Delta-файлов немного различаются между Run в формате строк и столбцов).
  • Кроме того, поддерживается метаданные для отслеживания расположения файлов. Эти метаданные включают пути к файлам, их размеры, количество кортежей и статус сжатия.
  • Размер Run (настраивается через параметр rowstore_size) можно оптимизировать под различные рабочие нагрузки.

1.1.4 Слияние и сборка мусора

  • Перекрывающиеся диапазоны данных между Run вызывают усилённое чтение и снижают производительность запросов. Когда количество Run на диске превышает пороговое значение, MARS3 объединяет несколько Run в один с помощью внешней сортировки. Этот процесс называется слиянием.

  • Во время слияния данные остаются доступными для чтения и записи:

    • Чтение обращается только к входным файлам слияния
    • Запись не мешает текущему слиянию
    • Операции чтения, записи и слияния не блокируют друг друга
  • После завершения слияния исходные Run помечаются как устаревшие на основе идентификатора транзакции и переходят в состояние освобождения.

1.1.5 Уровни

  • Для обеспечения того, чтобы входные файлы для слияния имели схожий размер (избегая слияния больших и малых файлов), Run организованы в уровни — до 10 уровней: L0, L1, L2, ..., L9.
  • Слияние запускается, когда количество Run на уровне достигает порога, или когда общий размер Run на уровне превышает лимит. Объединённый Run продвигается на следующий более высокий уровень.
  • Для ускорения продвижения Run допускается одновременное выполнение нескольких задач слияния на одном уровне.

1.1.6 BRIN-индекс MARS3

  • MARS3 поддерживает создание, удаление и добавление BRIN-индексов.
  • Каждый Run создаёт собственный независимый файл BRIN-индекса при создании.
  • Пример: CREATE INDEX brin_idx ON t1 USING mars3_brin(time,tag_id);

1.1.7 Сжатие

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

1.1.8 Поддержка MVCC

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

1.1.9 Ввод данных

  • Данные записываются в память через INSERT, затем сбрасываются в Run уровня L0.
  • Размер Run уровня L0 настраивается. Подробности см. в разделе Настройка ниже.

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

  • MARS3 использует DELETE для удаления. Удаления записываются в Delta-файл соответствующего Run и физически удаляются во время слияния Run.
  • MARS3 использует UPDATE для обновления. Обновление сначала удаляет старую запись, затем вставляет новую.
  • В режиме Unique MARS3 не поддерживает удаление. Обновления не требуют явного указания ключевых слов 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 при определении.

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

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

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

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

Примечание!
Создание BRIN-индекса на таблицах MARS3 поддерживается, но не обязательно.
Начиная с версии 6.3.0, требование использовать предложение ORDER BY при создании таблиц MARS3 было снято.


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

Примечание!
Эти параметры являются настройками на уровне таблицы. Они могут быть указаны только при создании таблицы с помощью предложения WITH(mars3options='a=1,b=2,...') (за исключением compress_threshold, который совместим с MARS2 и может быть задан напрямую через WITH). После установки их нельзя изменить. Эти настройки применяются к одной таблице. Подробнее см. Параметры таблиц базы данных.

Следующий параметр управляет размером Run в L0, косвенно влияя на размеры Run на более высоких уровнях.

Параметр Единица Значение по умолчанию Диапазон Описание
rowstore_size МБ 64 8 – 1024 Управляет моментом переключения Run уровня L0. Новый Run создаётся, когда объём данных превышает этот порог.

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

Параметр Единица Значение по умолчанию Диапазон Описание
compress_threshold Кортежи 1200 1 – 100000 Порог сжатия. Максимальное количество кортежей на столбец, сжимаемых за одну партию.

Следующий параметр задаёт режим загрузки данных в MARS3.

Параметр Единица Значение по умолчанию Диапазон Описание
prefer_load_mode normal normal / bulk Режим загрузки данных. normal означает нормальный режим: новые данные сначала записываются в Run в формате строк уровня L0, затем перемещаются в Run в столбцовом формате уровня L1 после достижения rowstore_size. Это требует дополнительного ввода-вывода по сравнению с bulk, а преобразование в столбцовый формат происходит асинхронно. Подходит для частых вставок малыми пакетами, когда ёмкость ввода-вывода достаточна, а задержка критична. bluk означает режим массовой загрузки: используется для редких, крупных пакетных вставок. Данные записываются напрямую в Run в столбцовом формате уровня L1, что снижает один ввод-вывод по сравнению с normal. Преобразование в столбцовый формат синхронно. Подходит для сценариев с ограниченной ёмкостью ввода-вывода и низкой чувствительностью к задержке.

Следующий параметр управляет коэффициентом усиления размера уровня.

Параметр Единица Значение по умолчанию Диапазон Описание
level_size_amplifier 8 1 – 1000 Коэффициент усиления размера уровня. Порог для запуска слияния уровня: rowstore_size * (level_size_amplifier ^ level). Более высокое значение замедляет чтение, но ускоряет запись. Выбирайте на основе рабочей нагрузки (преобладание записи/чтения, коэффициент сжатия и т.д.). Примечание: Избегайте чрезмерного количества Run на уровне, так как это может ухудшить производительность запросов или заблокировать новые вставки.

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

=# CREATE TABLE metrics (
    ts              timestamp,
    dev_id          bigint,
    power           float,
    speed           float
) USING MARS3
WITH (compress_threshold=1200,mars3options='rowstore_size=64',compresstype=zstd, compresslevel=1)
DISTRIBUTED BY (dev_id)
ORDER BY (dev_id,ts)
PARTITION BY RANGE (ts)
( START ('2023-07-01 00:00:00') INCLUSIVE
  END ('2023-08-01 00:00:00') EXCLUSIVE
  EVERY (INTERVAL '1 day')
,DEFAULT PARTITION OTHERS);

1.2.3 Вспомогательные функции

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


2 Обзор MARS2

Хранилище MARS2 разработано для высокоскоростной загрузки и запросов данных, используя упорядоченное хранение для устранения накладных расходов на поиск.

2.1 Внутренние принципы

Как и MARS3, MARS2 использует структуру LSM-дерева для хранения.

Ниже приведена схема внутренней структуры MARS2:

Рассмотрим эту схему по концепциям.

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

  • В MARS2 данные хранятся в отсортированном порядке. При создании таблицы порядок сортировки определяется созданием индекса. Столбцы, участвующие в этом порядке сортировки, называются ключом сортировки.
  • Ключ сортировки может быть определён только один раз и не может быть изменён или удалён.
  • Для максимального использования преимуществ упорядочивания данных выбирайте в качестве ключа сортировки часто используемые столбцы с высокой селективностью. Например, в таблице мониторинга устройств используйте временную метку события и идентификатор устройства.
  • Если ключ сортировки имеет текстовый тип и допустимо байтовое упорядочивание, применение COLLATE C к этому столбцу ускорит сортировку.

2.1.2 Run и метаданные

  • На основе ключа сортировки данные в MARS2 хранятся в отсортированном порядке. Непрерывный сегмент отсортированных данных называется Run.
  • Для поиска Run метаданные записывают их пути хранения.
  • Метаданные также хранят минимальные и максимальные значения Run, что позволяет эффективно фильтровать запросы.
  • При вставке данные сортируются в памяти, поэтому размер Run ограничен доступной памятью для сортировки.

2.1.3 Слияние

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

2.1.4 Уровни

  • Для обеспечения того, чтобы входные файлы для слияния имели схожий размер (избегая слияния больших и малых файлов), Run организованы в уровни: три уровня: L0, L1, L2.
  • Когда объединённый Run превышает порог размера, он продвигается на более высокий уровень:
    1. Новые вставки направляются в L0. Когда количество Run достигает порога (настраивается через mars2_automerge_threshold), запускается слияние L0, объединяющее все Run L0 в один.
    2. Если результирующий Run превышает 25 МБ (настраивается через level0_upgrade_size), он продвигается в L1.
    3. Если общий размер Run L1 превышает 1000 МБ (настраивается через level1_upgrade_size), запускается слияние L1, объединяющее все Run L1 в один.
    4. Если результирующий Run превышает 1000 МБ (настраивается через level1_upgrade_size), он продвигается в L2.
    5. Run в L2 не подвергаются дальнейшему слиянию.

2.1.5 Столбцовое хранение

  • MARS2 использует столбцовое хранение, считывая с диска только необходимые столбцы, что снижает I/O.
  • Однородные типы данных внутри столбца позволяют добиться лучшего сжатия и экономии дискового пространства.
  • Столбцовое хранение также поддерживает векторизованное выполнение, значительно ускоряя производительность запросов. См. Векторизованный движок выполнения.

2.1.6 Фильтрация MINMAX

  • Как упоминалось, метаданные Run хранят минимальные и максимальные значения для фильтрации запросов.
  • Поскольку фильтрация использует метаданные без загрузки фактических данных, стоимость I/O ниже, чем при последовательном сканировании (которое сначала загружает данные, а затем фильтрует), что обеспечивает более быструю фильтрацию.
  • MINMAX по умолчанию не записывается при создании таблицы MARS2; его необходимо явно объявить.

Сначала создайте расширение matrixts:

=# CREATE EXTENSION matrixts ;

Затем явно объявите запись min/max:

=# CREATE TABLE metrics (
    ts              timestamp   ENCODING(minmax),
    dev_id          bigint      ENCODING(minmax),
    power           float,
    speed           float,
    message         text
) USING MARS2;

Создайте индекс mars2_btree:

=# CREATE INDEX ON metrics
USING mars2_btree (ts, dev_id);
  • MINMAX может быть определён как для ключей сортировки, так и для не-ключей. Наиболее эффективен для ключей сортировки. MINMAX может быть записан в верхнем или нижнем регистре.
  • Максимум 32 столбца могут иметь определённый MINMAX.

2.1.7 Btree-индекс MARS2

  • Помимо MINMAX, MARS2 включает встроенный индекс (создаётся при создании таблицы).
  • Может быть определён только один индекс, то есть разрешён только один глобальный порядок сортировки.
  • В отличие от стандартных Btree-индексов, Btree-индекс MARS2:
    • Является разреженным, поэтому занимает меньше места
    • Избегает случайного I/O при сканировании индекса, поскольку базовые данные уже отсортированы

2.1.8 Сжатие

То же, что и в MARS3.

  • По умолчанию все столбцы сжимаются с использованием lz4.
  • Поддерживается ручное указание цепочек кодирования сжатия на уровне таблицы или столбца.

2.1.9 Ввод данных

  • Данные записываются в память через INSERT, затем обрабатываются в Run.
  • Если размер вставки превышает объем памяти для сортировки, создаётся несколько Run.
  • Размер памяти для сортировки настраивается. См. «2.2.2 Параметры конфигурации» ниже.

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

  • MARS2 в настоящее время не поддерживает операции обновления или удаления.
  • Обновления можно эмулировать с помощью режима Unique.
  • Удаление возможно только путём удаления или TRUNCATE партиционированных таблиц.

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

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

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

=# CREATE TABLE metrics (
    ts              timestamp,
    dev_id          bigint,
    power           float,
    speed           float,
    message         text
) USING MARS2;

=# CREATE INDEX ON metrics
USING mars2_btree (ts, dev_id);

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

Примечание!
Параметры на уровне таблицы можно задать только при создании таблицы с помощью предложения WITH, они применяются к одной таблице и не могут быть изменены позже. Глобальные параметры можно задавать на уровне сессии или системы. Изменения на уровне системы требуют mxstop -u для применения. Подробнее см. Параметры таблиц базы данных.

Следующие параметры управляют поведением слияния, как описано в разделе «Уровни» выше.

Параметр управления слиянием Единица Значение по умолчанию Диапазон Описание
mars2_automerge_threshold run 32 10 – 2048 Управляет триггером слияния для всех таблиц MARS2: количество L0 Run, при достижении которого запускается слияние. Глобальная настройка. Переопределение на уровне таблицы: level0_merge_threshold
level0_merge_threshold run 32 1 – 2048 Управляет порогом слияния L0 для каждой таблицы. Настройка на уровне таблицы
level0_upgrade_size MB 25 1 – 10000 Управляет размером перехода L0 → L1 для каждой таблицы. Если объединённый Run превышает этот порог, он повышается до L1. Настройка на уровне таблицы
level1_upgrade_size MB 1000 1 – 10000 Управляет размером перехода L1 → L2 для каждой таблицы. Если объединённый Run превышает этот порог, он повышается до L2. Настройка на уровне таблицы
  • Если частые вставки малого размера (каждая создаёт один Run) приводят к слишком частым слияниям, это неэффективно. Увеличьте mars2_automerge_threshold / level0_merge_threshold, чтобы снизить частоту слияний.
  • Если объединённые Run из L0/L1 слишком малы для повышения уровня, следующее слияние будет повторно обрабатывать их, вызывая запись с усилением. Чтобы избежать этого, уменьшите level0_upgrade_size / level1_upgrade_size.

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

Параметр управления сжатием Единица Значение по умолчанию Диапазон Описание
compress_threshold tuples 1200 1 – 100000 Порог сжатия. Количество кортежей на таблицу, сжимаемых за одну партию. Максимальное количество кортежей в единице сжатия. Настройка на уровне таблицы

Следующие параметры управляют размером памяти для сортировки при вставке. При вставке в несколько партиционированных таблиц каждая получает mars2_sort_mem_core; общее потребление не превышает mars2_sort_mem.

Параметр памяти для сортировки Единица Значение по умолчанию Диапазон Описание
mars2_sort_mem KB 2097152KB (2 ГБ) 128KB – 2147483647KB (~2048 ГБ) Память для сортировки на одну вставку. Для партиционированных таблиц разделяется между партициями. Глобальная настройка.
mars2_sort_mem_core KB 16384KB (16 МБ) 128KB – 2147483647KB (~2048 ГБ) Минимальная память для сортировки на одну партиционированную таблицу. Глобальная настройка.

Пример конфигурации на уровне таблицы:

=# CREATE TABLE metrics (
    ts              timestamp,
    dev_id          bigint,
    power           float,
    speed           float,
    message         text
) 
USING MARS2
WITH (compress_threshold=1200,level0_merge_threshold=32);

=# CREATE INDEX ON metrics
USING mars2_btree (ts, dev_id);

Пример глобального параметра на уровне сессии:

=# SET mars2_sort_mem TO 2097152;

Пример глобального параметра на уровне системы:

=# gpconfig -c mars2_sort_mem -v 2097152
=# \q
$ mxstop -u


3 Обзор HEAP

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

3.1 Механизм MVCC

При использовании MVCC таблицы HEAP не физически удаляют данные при обновлениях или удалениях. Вместо этого старые данные маскируются с помощью метаданных видимости версий. В результате частые обновления и удаления приводят к росту физического размера хранилища. Рекомендуется периодически выполнять VACUUM для освобождения места.

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

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

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


4 Обзор AO

Таблицы, использующие AOCO или AORO в качестве движков хранения, объединяются под общим названием AO (Append-Optimized) таблицы. Также известны как таблицы с оптимизацией для добавления; поддерживают операции вставки, обновления и удаления, а также сжатие.
AORO поддерживает хранение по строкам; AOCO поддерживает хранение по столбцам.

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

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

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