Зачем: дать практику учета новых связей пользователей при обновлении прежних дней витрины. Что: добавлены две связанные лабораторные с наблюдениями через Airflow, собственными таблицами student и повтором частично записанной загрузки. Обновлены программа, индекс и ссылки из справочников DDS и DM. Проверка: DDL и SQL под student на ClickHouse 26.3.17.56, четыре модельных дня через Airflow, полные сверки и повтор незавершенной загрузки; config-test, lint, локальные ссылки и независимое ревью.
10 KiB
Как обновлять витрину без полной пересборки
Чему учит: выбирать дни для пересчета по новым данным, проверять результат и повторять незавершенную загрузку.
Факультатив повышенной сложности продолжает работу
«Когда хранилище узнало пользователя».
Понадобятся ее запрос сохранения связей и таблица default.lab_identity_history.
Работайте под student через то же подключение DBeaver.
Стенд пересчитывает dm.daily_traffic_v целиком: новая связь может изменить
число известных пользователей в старом дне. Вы построите рядом свою витрину,
которая пересчитывает только затронутые дни и дает тот же результат.
В опыте появляются новые дни сессий и новые связи. Мир стенда не отзывает связи и не исправляет старые сессии при обычном продвижении времени. Удаление данных, исправления старых сессий и пересоздание мира потребовали бы другого способа обнаружения изменений.
Подготовьте витрину и журнал загрузок
Создайте две локальные таблицы. Если имена заняты, выберите свои.
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, если они больше не нужны.