Скользящие окна

Поскольку непрерывные представления непрерывно и инкрементально обновляются со временем, Tantor PipelineDB имеет возможность учитывать текущее время при обновлении результата непрерывного представления. Запросы, которые включают предложение WHERE с временным компонентом, относящимся к текущему времени, называются запросами со скользящим окном. Набор событий, который фильтрует или принимает скользящее предложение WHERE, постоянно меняется со временем.

Существуют два важных компонента предложения скользящего окна WHERE:

  • clock_timestamp() — встроенная функция, которая всегда возвращает текущую метку времени.

  • arrival_timestamp — специальный атрибут всех входящих событий, содержащий время, когда Tantor PipelineDB их получил, как описано в разделе Порядок поступления.

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

Эти концепции проще продемонстрировать на примерах.

Примеры

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

Каких пользователей я видел за последнюю минуту?

CREATE VIEW recent_users WITH (sw = '1 minute') AS
  SELECT user_id::integer FROM stream;

Внутри Tantor PipelineDB перепишет этот запрос следующим образом:

CREATE VIEW recent_users AS
  SELECT user_id::integer FROM stream
WHERE (arrival_timestamp > clock_timestamp() - interval '1 minute');

Примечание

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

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

Давайте разберем, что происходит с предикатом (arrival_timestamp > clock_timestamp() - interval '1 minute').

Каждый раз, когда оценивается clock_timestamp() - interval '1 minute', возвращается метка времени, отстающая на 1 минуту. Добавление arrival_timestamp и > означает, что этот предикат будет оцениваться как true, если arrival_timestamp для данного события отстает больше чем на 1 минуту. Поскольку предикат оценивается каждый раз, когда считывается новое событие, возникает скользящее окно с диапазоном в 1 минуту.

Примечание

Tantor PipelineDB поддерживает значения current_date, current_time и current_timestamp в запросах, но по задумке они не работают с запросами со скользящим окном, потому что они остаются постоянными в пределах транзакции и, следовательно, не обязательно актуальны на текущий момент времени.

Скользящие агрегатные функции

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

Давайте рассмотрим несколько примеров:

  • Сколько пользователей я видел за последнюю минуту?

    CREATE VIEW count_recent_users WITH (sw = '1 minute') AS
      SELECT COUNT(*) FROM stream;
    

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

  • Какова средняя скользящая температура моих датчиков за пять минут?

    CREATE VIEW sensor_temps WITH (sw = '5 minutes') AS
      SELECT sensor::integer, AVG(temp::numeric) FROM sensor_stream
    GROUP BY sensor;
    
  • Сколько уникальных пользователей было за последние 30 дней?

    CREATE VIEW uniques WITH (sw = '30 days') AS
      SELECT COUNT(DISTINCT user::integer) FROM user_stream;
    
  • Какова длительность ответа моих серверов за последние 5 минут в 99-м процентиле?

    CREATE VIEW latency WITH (sw = '5 minutes') AS
      SELECT server_id::integer, percentile_cont(0.99)
      WITHIN GROUP (ORDER BY latency::numeric) FROM server_stream
    GROUP BY server_id;
    

Временная инвалидация

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

  • Фоновая инвалидация — фоновый процесс, аналогичный autovacuumer PostgreSQL, периодически запускается и физически удаляет любые сроки с истекшим сроком из скользящих непрерывных представлений.

  • Инвалидация при чтении — когда данные из непрерывного представления запрашиваются с помощью SELECT, любые данные, которые слишком стары, отбрасываются динамически при формировании результата. При этом гарантируется, что даже если недействительные строки все еще существуют, они не попадают в результаты запроса.

step_factor

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

Например, запрос скользящего окна, который агрегируется по часам, на самом деле может на диске иметь данные, агрегированные по минутам, так что в окончательный агрегированный результат, возвращаемый читателям, включены только последние 60 минут. Эти внутренние, более детализированные уровни агрегации для запросов скользящего окна называются «ступеньками». Представление «накладывается» поверх этих «ступенек» для выполнения окончательной агрегации во время чтения.

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

step_factor

Целое число от 1 до 50, которое указывает ширину «ступеньки» скользящего окна в процентах от размера окна, заданного sw. Меньшее значение step_factor обеспечит более точное определение момента исключения данных из окна, но за счет увеличения размера таблицы материализованного представления на диске. Большее значение step_factor уменьшит размер таблицы материализованного представления на диске за счет уменьшения точности определения момента исключения из окна.

Вот пример использования step_factor в сочетании с sw для агрегации данных за час с шагом в 30 минут:

CREATE VIEW hourly (WITH sw = '1 hour', step_factor = 50)
  AS SELECT COUNT(*) FROM stream;