Выбор и преобразование движка хранения

1. Сценарий хранения временных данных

В сценарии работы с временными данными MatrixDB поддерживает два распространённых типа таблиц:

Тип Описание
Heap Таблица Heap — это тип хранения, предоставляемый традиционными базами данных PostgreSQL, также известный как кучные таблицы.
Этот тип таблиц поддерживает большое количество одновременных операций чтения и записи, транзакции, индексы и другие функции.
Mars Mars-таблица — это собственный тип хранения, разработанный компанией MatrixDB.
Поддерживает сжатие, колоночное хранение и предварительную агрегацию, обеспечивая исключительно высокую производительность в аналитических сценариях.
Однако данные должны вставляться по порядку, а операции обновления и удаления пока не поддерживаются.

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

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

2. Управление партициями и преобразование движков хранения

В MatrixDB технология партиционирования используется для объединения двух типов таблиц и разделения данных по временным меткам. Горячие данные сначала записываются в «горячую» партицию типа Heap. После того как становится ясно, что данные больше не будут изменяться и более ранние данные уже не поступят, «горячую» партицию Heap можно преобразовать в «холодную» партицию Mars.

Продолжительность партиций зависит от объема данных. Например: если объем данных большой, размер партиции должен быть меньше — например, разбивать по дням или часам; если объем данных небольшой, размер партиции можно увеличить — разбивать по неделям или месяцам.

MatrixDB предоставляет пользовательские функции (UDF) для управления партициями. Перед их использованием необходимо установить соответствующие расширения:

CREATE EXTENSION matrixts;
CREATE EXTENSION mars;

Рассмотрим пример записи объёма операций чтения и записи на диск. Ниже приведено использование горячего и холодного хранилищ:

2.1 Создание таблицы временных данных

CREATE TABLE disk(
    time timestamp with time zone,
    tag_id int,
    read float,
    write float
)
Distributed by (tag_id)
Partition by range(time);

С помощью указанного выше SQL-запроса создаётся таблица временных данных с партиционированием по временным меткам, ключ распределения — tag_id.

2.2 Создание шаблона партиции

После создания таблицы временных данных шаблон партиции создаётся вызовом функции build_timeseries_table, предоставляемой расширением mars:

SELECT mars.build_timeseries_table('disk','tagkey="tag_id", timekey="time", timebucket="1 day"');

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

Таблица шаблона партиции не удаляется автоматически при использовании обычной команды DROP TABLE и требует ручной очистки. Если вы хотите удалить исходную таблицу, правильная последовательность действий следующая:

SELECT mars.destroy_timeseries_table('disk');
DROP TABLE disk;

2.3 Создание партиции

После создания шаблона партиции вызывается функция add_partition, предоставляемая расширением mars, чтобы создать партицию:

SELECT mars.add_partition('disk', '2021-04-01', '2021-05-01','1 day');

Как указано выше, данный SQL-запрос создаёт партицию с 1 апреля 2021 года по 1 мая 2021 года с шагом в один день.

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

ymatrix=# SELECT * from mars.list_partition('disk');
                    relname                    | storage
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 heap_disk_20210401000000000_20210402000000000 | heap
 heap_disk_20210402000000000_20210403000000000 | heap
 heap_disk_20210403000000000_20210404000000000 | heap
 heap_disk_20210404000000000_20210405000000000 | heap
 heap_disk_20210405000000000_20210406000000000 | heap
 heap_disk_20210406000000000_20210407000000000 | heap
 heap_disk_20210407000000000_20210408000000000 | heap
 heap_disk_20210408000000000_20210409000000000 | heap
 heap_disk_20210409000000000_20210410000000000 | heap
 heap_disk_20210410000000000_20210411000000000 | heap
 heap_disk_20210411000000000_20210412000000000 | heap
 heap_disk_20210412000000000_20210413000000000 | heap
 heap_disk_20210413000000000_20210414000000000 | heap
 heap_disk_20210414000000000_20210415000000000 | heap
 heap_disk_20210415000000000_20210416000000000 | heap
 heap_disk_20210416000000000_20210417000000000 | heap
 heap_disk_20210417000000000_20210418000000000 | heap
 heap_disk_20210418000000000_20210419000000000 | heap
 heap_disk_20210419000000000_20210420000000000 | heap
 heap_disk_20210420000000000_20210421000000000 | heap
 heap_disk_20210421000000000_20210422000000000 | heap
 heap_disk_20210422000000000_20210423000000000 | heap
 heap_disk_20210423000000000_20210424000000000 | heap
 heap_disk_20210424000000000_20210425000000000 | heap
 heap_disk_20210425000000000_20210426000000000 | heap
 heap_disk_20210426000000000_20210427000000000 | heap
 heap_disk_20210427000000000_20210428000000000 | heap
 heap_disk_20210428000000000_20210429000000000 | heap
 heap_disk_20210429000000000_20210430000000000 | heap
 heap_disk_20210430000000000_20210501000000000 | heap
(30 rows)

Как показано в SQL-запросе выше, отображаются все подтаблицы-партиции, входящие в таблицу disk, включая имена подтаблиц и типы хранения.

2.4 Запись данных временного ряда

insert into disk values(now(), ...., ..., ...);

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

2.5 Замена партиций

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

Расширение Mars предоставляет функцию compress_partition, предназначенную для преобразования партиций:

ymatrix=# select mars.compress_partition('heap_disk_20210401000000000_20210402000000000');
              compress_partition
----------------------------------------------------------------------------------------------------------------------------------
 mars_disk_20210401000000000_20210402000000000
(1 row)

Как указано выше, SQL-запрос выполняет преобразование партиции за 1 апреля. После преобразования вызовите функцию list_partition, чтобы проверить имя и тип хранения преобразованной партиции:

ymatrix=# SELECT * from mars.list_partition('disk');
                    relname                    | storage
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 heap_disk_20210402000000000_20210403000000000 | heap
 heap_disk_20210403000000000_20210404000000000 | heap
 heap_disk_20210404000000000_20210405000000000 | heap
 heap_disk_20210405000000000_20210406000000000 | heap
 heap_disk_20210406000000000_20210407000000000 | heap
 heap_disk_20210407000000000_20210408000000000 | heap
 heap_disk_20210408000000000_20210409000000000 | heap
 heap_disk_20210409000000000_20210410000000000 | heap
 heap_disk_20210410000000000_20210411000000000 | heap
 heap_disk_20210411000000000_20210412000000000 | heap
 heap_disk_20210412000000000_20210413000000000 | heap
 heap_disk_20210413000000000_20210414000000000 | heap
 heap_disk_20210414000000000_20210415000000000 | heap
 heap_disk_20210415000000000_20210416000000000 | heap
 heap_disk_20210416000000000_20210417000000000 | heap
 heap_disk_20210417000000000_20210418000000000 | heap
 heap_disk_20210418000000000_20210419000000000 | heap
 heap_disk_20210419000000000_20210420000000000 | heap
 heap_disk_20210420000000000_20210421000000000 | heap
 heap_disk_20210421000000000_20210422000000000 | heap
 heap_disk_20210422000000000_20210423000000000 | heap
 heap_disk_20210423000000000_20210424000000000 | heap
 heap_disk_20210424000000000_20210425000000000 | heap
 heap_disk_20210425000000000_20210426000000000 | heap
 heap_disk_20210426000000000_20210427000000000 | heap
 heap_disk_20210427000000000_20210428000000000 | heap
 heap_disk_20210428000000000_20210429000000000 | heap
 heap_disk_20210429000000000_20210430000000000 | heap
 heap_disk_20210430000000000_20210501000000000 | heap
 mars_disk_20210401000000000_20210402000000000 | mars
(30 rows)

Как видно, префикс имени преобразованной партиции изменился с heap на mars, а тип хранения также был соответственно изменён.

2.6 Удаление старых таблиц

Функция compress_partition выполняет преобразование партиций горячих и холодных данных, однако очистка исходной подтаблицы Heap должна выполняться вручную:

DROP TABLE heap_disk_20210401000000000_20210402000000000;

3. Прямое использование движка Mars

Движок Mars можно использовать не только в партиционированных таблицах, но и отдельно — создавать и использовать его автономно.

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

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

CREATE EXTENSION matrixts;
CREATE EXTENSION mars;

Затем создайте таблицу Mars. При создании таблицы используйте USING Mars, чтобы указать движок хранения:

CREATE TABLE disk_mars(
    time timestamp with time zone,
    tag_id int,
    read float,
    write float
)
USING Mars
WITH (tagkey="tag_id", timekey="time", timebucket="8 hours")
Distributed by (tag_id);

Метаданные таблицы Mars указываются в ключевом слове WITH, включая:

  1. tag_id: поле идентификатора устройства
  2. timekey: поле временной метки
  3. timebucket: временной интервал каждой группы строк (Row Group)

3.1 Сжатое хранение

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

Таблица Mars поддерживает 4 типа сжатия:

  1. rle_type
  2. zstd
  3. zlib
  4. lz4

При создании таблицы Mars тип сжатия столбца указывается через параметр compressiontype. Одновременно необходимо указать уровень сжатия (от 0 до 9) с помощью параметра compresslevel. Чем выше уровень, тем выше степень сжатия и меньше занимаемое пространство, но при этом возрастает нагрузка на вычислительные ресурсы.

Ниже приведён SQL-запрос, создающий таблицу Mars и задающий различные типы сжатия для разных столбцов:

CREATE TABLE disk_mars (
    time timestamp with time zone ENCODING (compresstype=rle_type, compresslevel=1),
    tag_id int ENCODING (compresstype=zstd, compresslevel=1),
    read float ENCODING (compresstype=zlib, compresslevel=1),
    write float ENCODING (compresstype=lz4, compresslevel=1)
)
USING Mars
WITH (tagkey="tag_id", timekey="time", timebucket="8 hours")
Distributed by (tag_id);

3.2 Загрузка данных в таблицу Mars

Таблица Mars требует, чтобы данные были отсортированы по tag_id и временной метке перед загрузкой, в противном случае возможны ошибки в результатах запросов.

Импорт данных из таблицы Heap в таблицу Mars:

INSERT INTO disk_mars SELECT * FROM disk_heap ORDER BY tag_id, time;

Также можно импортировать данные из внешней таблицы в таблицу Mars:

INSERT INTO disk_mars SELECT * FROM ext ORDER BY tag_id, time;