Лямбда-представления для дашбордов с данными почти в реальном времени
На этой странице в примерах кода используется Snowflake, но вы можете адаптировать паттерн лямбда-представлений для других хранилищ данных.
Паттерн лямбда-представлений объединяет таблицу фактов, обновляемую пакетно или инкрементально, с небольшим срезом самых свежих данных, поступающих почти в реальном времени (near real-time, NRT), и предоставляет доступ к ним через единое представление. Это устаревший, но всё ещё полезный паттерн, который некоторые команды использовали для создания оперативных дашбордов с данными почти в реальном времени на базе dbt и хранилища данных.
Когда использовать лямбда-представления
- Вам нужны более свежие данные, чем позволяет обычное расписание инкрементального обновления, но
- Вы не можете (или не хотите) использовать динамические таблицы или материализованные представления либо хотите полностью сохранить логику в SQL-коде dbt. Примеры на этой странице предполагают следующую конфигурацию:
Исходные условия
Примеры на этой странице предполагают следующую конфигурацию:
- Необработанные события непрерывно поступают в
raw.eventsс помощью механизма потоковой загрузки вашего хранилища данных (например, Snowpipe, Databricks Auto Loader, Kafka или аналогичного механизма загрузки). - У вас уже есть инкрементальная таблица фактов, которая обновляется каждые несколько минут с использованием
incremental_strategy='merge'. - Большинству дашбордов достаточно данных из этой инкрементальной таблицы, но небольшой группе оперативных дашбордов нужны данные «на текущий момент» (например, события за последние несколько минут).
Как работает этот паттерн
- Базовая инкрементальная таблица обновляется каждые несколько минут с использованием
incremental_strategy='merge'. - NRT-представление выбирает только события новее максимального значения
event_ts, уже сохранённого в базовой инкрементальной таблице. - Лямбда-представление объединяет базовую таблицу и NRT-представление с помощью
UNION ALL, устраняя дубликаты строк на основе семантики первичного ключа.
Нижележащие BI-инструменты и дашборды обращаются только к лямбда-представлению.
Базовая инкрементальная таблица
В качестве базовой таблицы можно использовать инкрементальную модель с merge из паттерна 1 для Snowflake; для полноты приведём её здесь:
-- models/fct_events.sql
{{ config(
materialized = 'incremental',
incremental_strategy = 'merge',
unique_key = 'event_id',
cluster_by = ['event_date'],
snowflake_warehouse = 'TRANSFORM_WH'
) }}
with source_events as (
select
event_id,
event_ts::timestamp_ntz as event_ts,
to_date(event_ts) as event_date,
user_id,
event_type,
payload
from {{ source('raw', 'events') }}
{% if is_incremental() %}
-- Only pull new/changed rows since last successful load
where event_ts >
(select max(event_ts) from {{ this }})
{% endif %}
)
select *
from source_events;
Настройте запуск этой модели, например, каждые 5–15 минут в составе задания для обработки данных почти в реальном времени.
NRT-представление: строки новее данных в базовой таблице
NRT-представление возвращает только события со значением event_ts, превышающим максимальную временную метку в базовой таблице, поэтому пересечения данных и двойного учёта не возникает:
-- models/fct_events_nrt.sql
{{ config(
materialized = 'view'
) }}
with base_max as (
select max(event_ts) as max_event_ts
from {{ ref('fct_events') }}
),
fresh_events as (
select
e.event_id,
e.event_ts::timestamp_ntz as event_ts,
to_date(e.event_ts) as event_date,
e.user_id,
e.event_type,
e.payload
from {{ source('raw', 'events') }} as e
cross join base_max
where e.event_ts > base_max.max_event_ts
)
select *
from fresh_events;
Особенности:
- Расписание не требуется — это обычное представление над
raw.eventsс фильтрацией поmax(event_ts)изfct_events. - Каждый запрос к
fct_events_nrtсканирует только данные, поступившие «с момента последнего пакетного обновления»: это должен быть небольшой временной интервал (например, несколько минут или часов в зависимости от частоты запуска задания).
Лямбда-представление: единая точка чтения данных для BI
Лямбда-представление объединяет исторические данные из базовой инкрементальной таблицы с самыми свежими событиями из NRT-представления.
-- models/fct_events_lambda.sql
{{ config(
materialized = 'view'
) }}
select
event_id,
event_ts,
event_date,
user_id,
event_type,
payload
from {{ ref('fct_events') }}
union all
select
event_id,
event_ts,
event_date,
user_id,
event_type,
payload
from {{ ref('fct_events_nrt') }};
Настройте BI-инструменты и дашборды на чтение из analytics.fct_events_lambda. Большая часть данных поступает из предварительно рассчитанной инкрементальной таблицы, а самые свежие события (с момента последнего запуска dbt) — из запроса к raw.events, выполняемого в момент обращения.
Этот подход изложен в исходной публикации о лямбда-представлениях в dbt, где описано, как команды, в том числе JetBlue, создавали оперативные дашборды с данными почти в реальном времени на базе Snowflake и dbt.
Что следует учитывать
При использовании этого паттерна учитывайте следующее:
- Структура затрат
- Каждый запрос к
fct_events_lambdaдолжен читать NRT-срез изraw.eventsв дополнение к базовой таблице. - Используйте этот паттерн только для действительно оперативных дашбордов, потребности которых оправдывают дополнительные затраты на каждый запрос.
- Каждый запрос к
- Свежесть данных
- Свежесть данных ограничена:
- Частотой запуска инкрементального задания dbt (давностью обновления
fct_events), а также - Задержкой загрузки в
raw.events(Snowpipe / слой потоковой загрузки).
- Частотой запуска инкрементального задания dbt (давностью обновления
- Свежесть данных ограничена:
- Сложность в сравнении с альтернативами
- Во многих современных реализациях на Snowflake динамическая таблица или материализованное представление с небольшим значением
target_lagмогут обеспечить аналогичные соглашения об уровне обслуживания по принципу «данные всегда не старше X минут», при этом требуя меньше собственного SQL-кода и передавая управление инкрементальной логикой хранилищу данных. - Лямбда-представления лучше рассматривать как продвинутый / устаревший паттерн, который по-прежнему можно использовать, если вы:
- Хотите сохранить всю логику в SQL-коде dbt
- Не располагаете нужной возможностью хранилища данных в своей среде
- Расширяете существующую реализацию, уже построенную таким образом
- Во многих современных реализациях на Snowflake динамическая таблица или материализованное представление с небольшим значением