Непрерывные представления

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

После того, как строка потока прочитывается непрерывными представлениями, которые должны ее прочитать, она удаляется. Сырые, детальные данные не сохраняются. Единственные данные, которые сохраняются для непрерывного представления — это те, что возвращаются при выполнении SELECT * FROM that_view. Таким образом, непрерывное представление можно рассматривать как очень производительное материализованное представление в режиме реального времени.

Создание непрерывных представлений

Непрерывные представления создаются как представления PostgreSQL с параметром action=materialize. Вот синтаксис для создания непрерывного представления:

CREATE VIEW name [WITH (action=materialize [, ...])]  AS query

Примечание

По умолчанию параметр action принимает значение materialize, и поэтому action можно опустить при создании непрерывных представлений. Пока выбран поток, Tantor PipelineDB будет интерпретировать оператор CREATE VIEW с action=materialize.

Где query является подмножеством оператора SELECT PostgreSQL:

SELECT [ DISTINCT [ ON ( expression [, ...] ) ] ]
    expression [ [ AS ] output_name ] [, ...]
    [ FROM from_item [, ...] ]
    [ WHERE condition ]
    [ GROUP BY expression [, ...] ]

Где, в свою очередь:

  • from_item может быть одним из следующих вариантов:

    • stream_name [ [ AS ] alias [ ( column_alias [, ...] ) ] ]

    • table_name [ [ AS ] alias [ ( column_alias [, ...] ) ] ]

    • from_item [ NATURAL ] join_type from_item [ ON join_condition ]

    Примечание

    Этот раздел ссылается на потоки, которые похожи на таблицы и являются источниками данных для непрерывных представлений и преобразований, указываемыми в предложении FROM. Более подробная информация по ним приведена в разделе Потоки, но для начала их можно воспринимать как append-only таблицы — таблицы только для добавления данных.

  • expressionexpression или grouping sets specification PostgreSQL.

  • output_name — необязательный идентификатор для именования выражения.

  • condition — выражение, которое выдает результат типа boolean. Любая строка, которая не удовлетворяет этому условию, будет исключена из вывода. Строка удовлетворяет условию, если она возвращает true, когда фактические значения строк заменяются ссылками на переменные.

Примечание

Это касается только синтаксиса создания непрерывных представлений. Чтобы узнать больше о семантике каждого из этих элементов запроса, ознакомьтесь с документацией PostgreSQL SELECT.

Удаление непрерывных представлений

Чтобы удалить (DROP) непрерывное представление из системы, используйте команду DROP VIEW. Синтаксис команды прост:

DROP VIEW name

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

Очищение непрерывных представлений

Для удаления всех данных непрерывного представления без его удаления можно использовать функцию truncate_continuous_view:

SELECT truncate_continuous_view('name');

Эта команда эффективно удалит все строки непрерывных представлений аналогично команде PostgreSQL TRUNCATE.

Просмотр непрерывных представлений

Чтобы просмотреть непрерывные представления и их текущие определения в системе, можно выполнить следующий запрос:

SELECT * FROM pipelinedb.views;

Извлечение данных

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

SELECT * FROM some_continuous_view

user

event_count

a

10

b

20

c

30

Любой оператор SELECT допустим для непрерывного представления, что позволяет выполнять дальнейший анализ постоянно обновляемого содержимого представлений:

SELECT t.name, sum(v.value) + sum(t.table_value) AS total
FROM some_continuous_view v JOIN some_table t ON v.id = t.id GROUP BY t.name

name

total

usman

10

jeff

20

derek

30

Истечение времени жизни (TTL)

Распространенным механизмом Tantor PipelineDB является включение временного столбца в агрегированные группировки и удаление старых строк, которые больше не нужны, таким образом, как определено в этом столбце. Хотя существует несколько способов достижения такого поведения, Tantor PipelineDB предоставляет встроенный контроль истечения срока строк с помощью критериев времени жизни (TTL), указанных на уровне непрерывного представления.

Контроль срока TTL может быть настроен для непрерывных представлений с помощью параметров хранения ttl и ttl_column. Истечение срока действия контролируется одним или несколькими процессами «сборщика», которые будут удалять (DELETE) любые строки со значением ttl_column старше интервала, указанного в ttl (относительно времени расчета). Вот пример определения непрерывного представления, которое укажет сборщику удалить все строки, где столбец minute старше одного месяца:

CREATE VIEW v_ttl WITH (ttl = '1 month', ttl_column = 'minute') AS
  SELECT minute(arrival_timestamp), COUNT(*) FROM some_stream GROUP BY minute;

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

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

Изменение TTL

Значения TTL могут быть добавлены, изменены и удалены из непрерывного представления с помощью функции pipelinedb.set_ttl:

pipelinedb.set_ttl ( cv_name, ttl, ttl_column )

Обновляет TTL данного непрерывного представления с помощью заданных параметров. ttl — это интервал, выраженный в виде строки (например, 1 day), а ttl_column — это имя столбца на основе метки времени.

Передача NULL для обоих параметров ttl и ttl_column эффективно удалит TTL из заданного непрерывного представления. Обратите внимание, что TTL не может быть изменен или удален из непрерывного представления со скользящим окном.

Активация и деактивация

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

Данный уровень контроля обеспечивается командами activate и deactivate, которые являются синонимами «пуска» и «паузы». Когда непрерывные представления запущены, они активно читают данные из входных потоков и соответственно постепенно обновляют свои результаты. И наоборот, непрерывные представления на паузе не читают данные из входных потоков и не обновляют результаты. Tantor PipelineDB продолжает работать, когда непрерывные представления на паузе, а сами непрерывные представления все еще можно просматривать — они просто не обновляются.

Сигнатуры функций принимают только название непрерывного представления или трансформации:

SELECT pipelinedb.activate('continuous_view_or_transform');
SELECT pipelinedb.deactivate('continuous_view_or_transform');

Непрерывные преобразования также могут быть активированы и деактивированы.

Важно

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

Подробнее смотрите в разделе Операционные функции.

Партицирование

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

Tantor PipelineDB автоматически создает партиции по мере необходимости на основе фактических значений данных, поступающих из потоков.

Поддерживается только подмножество функций партицирования PostgreSQL. Непрерывные представления могут быть разделены на диапазоны по столбцу, основанному на метке времени, с использованием фиксированных временных интервалов. Далее приведен синтаксис для создания партицированного непрерывного представления:

CREATE VIEW name WITH (partition_by = column, partition_duration = 'interval') AS query;

Где partition_by должен ссылаться на столбец timestamp или timestamptz, который должен являться одним из столбцов вывода query, а partition_duration — это указание диапазона партицирования в формате типа данных PostgresQL interval.

Например, следующее определение создает непрерывное представление, разделенное по столбцу ts, где каждая партиция соответствует интервалу в один месяц:

CREATE VIEW v_part WITH (partition_by = ts, partition_duration = '1 month')
  AS SELECT ts, COUNT(*) FROM stream GROUP BY ts;

В отличие от таблиц PostgreSQL разделенных по диапазонам, непрерывные представления в настоящее время не поддерживают ручное определение границ диапазона. Tantor PipelineDB автоматически назначает границы диапазона партицирования следующим образом:

  • Для некоторых распространенных интервалов партицирования выполняется обработка особых случаев, чтобы пользователи получали предсказуемые и ожидаемые границы партицирования. Например, с интервалом партицирования 1 week пользователи ожидают, что границы каждого партицирования на недели будут соответствовать началу и концу календарной недели. Это также обеспечивает разумные ожидания производительности, поскольку пользователи обычно предпочитают, чтобы одна календарная неделя занимала не более одной партиции. Ниже приведен список всех поддерживаемых особых случаев:

    • 1 year

    • 1 month

    • 1 week

    • 1 day

    • 1 hour

    • 1 minute

    • 1 second

    Например, для строки с временной меткой '2024-04-11 13:53:07.82049' Tantor PipelineDB выберет (и автоматически создаст, если необходимо) партицию с границами ['2024-01-01', '2025-01-01'), если partition_duration составляет 1 year, или ['2024-04-01', '2024-05-01'), если partition_duration составляет 1 month и так далее.

  • Для прочих интервалов границы диапазона выбираются автоматически путем округления до ближайших нижних и верхних временных отметок с указанной степенью партицирования. Например, с интервалом партицирования 3 minutes и значением временной отметки 2024-04-11 13:53:07.82049 Tantor PipelineDB выберет партицию с соответствующими границами диапазона ['2024-04-11 13:51:00', '2024-04-11 13:54:00') и создаст ее, если такая партиция еще не существует.

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

Альтернативные методы доступа

PostgreSQL поддерживает альтернативные механизмы хранения данных на диске, называемые методами доступа к таблицам. Tantor PipelineDB использует стандартный метод доступа «heap» для создания на диске таблиц и секций для материализованных представлений, но после их создания пользователи могут настроить для них другие методы доступа. Например, пользователю может понадобиться хранить исторические данные, предназначенные только для чтения, в определенных секциях в более компактном формате, который обеспечивается расширением Columnar. Такой формат также лучше подходит для аналитических запросов.

Действуют следующие ограничения:

  • Обновление любых данных на диске, хранящихся не в «куче», в настоящее время не поддерживается Tantor PipelineDB. Если комбинирующий процесс сталкивается с таблицей не вида heap, которую необходимо дополнить входящими потоковыми данными, он выдает ошибку "unsupported access method for relation" (неподдерживаемый метод доступа для отношения) и откатывает текущую транзакцию, отбрасывая входящие данные в текущем пакете. На практике это означает, что только секции со старыми, историческими данными, доступными только для чтения, могут быть безопасно преобразованы в альтернативные методы доступа, такие как Columnar. Обновления таких секций будут потеряны, возможно, вместе с обновлениями обычных таблиц heap в том же пакете.

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

    • START TRANSACTION

    • LOCK TABLE ... IN EXCLUSIVE MODE для целевой секции

    • создайте новую таблицу в той же схеме c новым методом доступа

    • скопируйте данные из старой секции в новую таблицу, например, с помощью INSERT INTO ... SELECT * FROM ...

    • отсоедините старую секцию в отдельную таблицу с помощью ALTER TABLE ... DETACH PARTITION ... CONCURRENTLY

    • при необходимости, можно удалить отсоединенную секцию с помощью DROP TABLE, если не нужно сохранить ее для целей резервного копирования

    • переименуйте новую таблицу, чтобы она имела то же имя, что и старая.

    • присоедините новую таблицу на место старой секции с помощью ALTER TABLE ... ATTACH PARTITION

    • COMMIT

Примеры

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

Важно

Единственные данные, которые Tantor PipelineDB сохраняет для непрерывного представления — это те, что вернулись бы при выполнении SELECT * FROM my_cv в нем (плюс небольшое количество метаданных). Это относительно новая концепция, но именно она делает непрерывные представления таким мощным инструментом.

Непрерывное представление будет хранить только одну строку в Tantor PipelineDB (всего несколько байтов), даже если он прочитает триллион событий за время работы:

CREATE VIEW avg_of_forever AS SELECT AVG(x) FROM one_trillion_events_stream;
  • Вычисляет количество уникальных пользователей, прошедших по url-ссылке, в день, используя только постоянный объем памяти в день:

    CREATE VIEW uniques AS
    SELECT date_trunc('day', arrival_timestamp) AS day,
      referrer, COUNT(DISTINCT user_id)
    FROM users_stream GROUP BY day, referrer;
    
  • Вычисляет линейную регрессию потока точек данных, сгруппированных по минутам:

    CREATE VIEW lreg AS
    SELECT date_trunc('minute', arrival_timestamp) AS minute,
      regr_slope(y, x) AS mx,
      regr_intercept(y, x) AS b
    FROM datapoints_stream GROUP BY minute;
    
  • Сколько показов рекламы было осуществлено за последние пять минут?

    CREATE VIEW imps AS
      SELECT COUNT(*) FROM imps_stream
    WHERE (arrival_timestamp > clock_timestamp() - interval '5 minutes');
    
  • Каковы 90-й, 95-й и 99-й процентили задержки запроса моих серверов?

    CREATE VIEW latency AS
      SELECT percentile_cont(array[90, 95, 99]) WITHIN GROUP (ORDER BY latency)
    FROM latency_stream;