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

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

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

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

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

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

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

Примеры

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

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

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

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

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

Примечание

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 минуту.

Примечание

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

Скользящие агрегаты

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

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

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

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

Каждый раз, когда выполняется SELECT на этом непрерывном представлении, возвращаемое количество будет количеством событий, увиденных за последнюю минуту. Например, если события перестали поступать, количество будет уменьшаться каждый раз, когда выполняется SELECT на непрерывном представлении. Это поведение работает для всех агрегатов :ref:`aggregates`которые поддерживает 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;

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

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

  • Фоновая инвалидация - фоновый процесс, аналогичный автовакууму 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;

Теперь, когда вы знаете, как работают запросы со скользящим окном, пора перейти к непрерывным соединениям JOIN.