Files
clickstream-data-platform/docs/labs/optional/incremental-daily-traffic.md
T
ddadmin b839a671a1 docs(labs): добавлены история связей и инкрементальный пересчет
Зачем: дать практику учета новых связей пользователей при обновлении
прежних дней витрины.

Что: добавлены две связанные лабораторные с наблюдениями через Airflow,
собственными таблицами student и повтором частично записанной загрузки.
Обновлены программа, индекс и ссылки из справочников DDS и DM.

Проверка: DDL и SQL под student на ClickHouse 26.3.17.56, четыре модельных
дня через Airflow, полные сверки и повтор незавершенной загрузки;
config-test, lint, локальные ссылки и независимое ревью.
2026-09-22 05:57:11 -04:00

147 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Как обновлять витрину без полной пересборки
**Чему учит:** выбирать дни для пересчета по новым данным, проверять результат
и повторять незавершенную загрузку.
Факультатив повышенной сложности продолжает работу
[«Когда хранилище узнало пользователя»](identity-over-time.md).
Понадобятся ее запрос сохранения связей и таблица `default.lab_identity_history`.
Работайте под `student` через то же подключение DBeaver.
Стенд пересчитывает `dm.daily_traffic_v` целиком: новая связь может изменить
число известных пользователей в старом дне. Вы построите рядом свою витрину,
которая пересчитывает только затронутые дни и дает тот же результат.
В опыте появляются новые дни сессий и новые связи. Мир стенда не отзывает
связи и не исправляет старые сессии при обычном продвижении времени.
Удаление данных, исправления старых сессий и пересоздание мира потребовали бы
другого способа обнаружения изменений.
## Подготовьте витрину и журнал загрузок
Создайте две локальные таблицы. Если имена заняты, выберите свои.
```sql
CREATE TABLE default.lab_daily_traffic
(
report_date Date,
visitors UInt64,
known_users UInt64,
observation_day Date
)
ENGINE = ReplacingMergeTree(observation_day)
ORDER BY report_date;
CREATE TABLE default.lab_traffic_runs
(
observation_day Date
)
ENGINE = MergeTree
ORDER BY observation_day;
```
В витрине одна актуальная строка на день. Для обновления вставляйте полный
новый результат дня с новой `observation_day`; читайте его с `FINAL`.
Движок выбирает максимальную `observation_day` для каждого `report_date`.
Старая версия может исчезнуть при слиянии: эта таблица хранит текущий расчет,
а не историю наблюдений.
Журнал хранит даты успешно завершенных загрузок. Добавляйте дату только
после записи всех выбранных дней и успешной сверки. Последняя строка журнала
будет границей, от которой вы ищете новые связи в следующем прогоне.
## Выполните первую загрузку целиком
Убедитесь, что `world_live` стоит на паузе и нет активных прогонов генерации
и обработки. Запустите `etl_pipeline` и дождитесь успеха. Получите текущую
`observation_day` способом из первой работы. Если такого наблюдения еще нет
в вашей истории, сохраните его; уже сохраненную дату повторно не вставляйте.
Напишите `INSERT INTO ... SELECT`, который рассчитывает все даты сессий из
`dds.session_v` со связями текущего наблюдения. Правила результата:
| Колонка | Что считать |
|---|---|
| `report_date` | `session_date` |
| `visitors` | число разных `client_id` |
| `known_users` | число разных известных `user_id` |
| `observation_day` | дата текущего наблюдения |
Сессии без известного пользователя сохраняйте. Несколько кук одного
пользователя не должны увеличивать `known_users`. Для соединения с локальной
историей связей используйте `GLOBAL LEFT JOIN`.
Построчно сверьте свои `report_date`, `visitors`, `known_users` с
`dm.daily_traffic_v`. Сверка должна находить лишние и отсутствующие даты
и различия обоих показателей. Учебную витрину читайте с `FINAL`.
После совпадения запишите дату наблюдения в журнал один раз.
## Выберите дни следующего пересчета
Продвиньте мир на один день: `world_next_day` с `days = 1`, затем
`etl_pipeline`; после каждого запуска дождитесь успеха. Сохраните новое
наблюдение связей запросом из первой работы.
Обозначим дату последней успешной загрузки из журнала **P**, текущую дату
наблюдения — **H**. До записи результата составьте запрос, возвращающий
разные даты для пересчета. Он должен учесть два изменения:
- в сессиях появились дни, которых еще нет в вашей витрине;
- в карте появились пары, которых не было при P; у этих кук есть сессии,
в том числе за прежние дни.
Сначала выведите даты и разберите одну старую дату: какая новая пара привела
к ее выбору и какие сессии с ней связаны? Рядом посчитайте, сколько дат
пересчитала бы полная загрузка. Зафиксируйте оба числа; равенство тоже
возможно, если новые связи затронули всю историю.
Проверьте альтернативу: искать изменения по `first_purchase_at`, взяв покупки
только после конца P. Сравните полученный набор пар с разностью наблюдений.
Покажите пару, которую такое ограничение пропускает.
## Обновите выбранные дни
Напишите второй `INSERT INTO ... SELECT`. Для выбранных дат он должен
рассчитать все сессии с полной картой связей на H. Прибавление числа новых
связей к прежнему `known_users` не заменяет пересчет разных пользователей.
Вставьте результаты с версией H. Полностью сверьте актуальную учебную витрину
со стендовой, включая дни, которые не пересчитывали. Только после совпадения
добавьте H в журнал.
Без продвижения мира повторите выбор дат. После успешной загрузки новых дат
и пар быть не должно, а результат витрины должен остаться прежним.
Повторно записывать H в журнал не требуется.
## Повторите незавершенную загрузку
Снова запустите `world_next_day` с `days = 1`, затем `etl_pipeline`,
дожидаясь успеха каждого. Сохраните новое наблюдение связей.
Получите даты для пересчета, но запишите результат только за одну из
них. Остальные пока не загружайте и дату в журнал не добавляйте.
Так вы воспроизведете остановку между записями дней, не ломая стенд.
Запустите свой обычный расчет еще раз, определив P по журналу. Он должен
довести загрузку до совпадения со стендовой витриной без очистки уже
записанных строк. После сверки запишите H в журнал.
Объясните на этом опыте, почему последнее сохраненное наблюдение нельзя
автоматически считать загруженным в витрину. Что изменится, если записать H
в журнал до обновления витрины?
## Что должно получиться
Сохраните SQL первой загрузки, выбора дат, обновления и сверки.
После двух новых модельных дней ваша витрина совпадает со стендовой,
повтор без новых данных сохраняет результат, а незавершенную загрузку
можно продолжить без полной пересборки.
Сравните цену двух вариантов: сколько дней вы пересчитывали и какие
дополнительные данные хранили. По этому опыту можно судить об объеме работы,
но не об ускорении: на небольшом стенде полный пересчет может быть дешевле.
В вашей реализации поиск связей пока читает всю историю покупок —
инкрементальным стал именно пересчет дневной витрины.
После работы удалите свои `lab_daily_traffic`, `lab_traffic_runs` и
`lab_identity_history` через `DROP TABLE`, если они больше не нужны.