docs(labs): упрощен язык учебных заданий
- Зачем: начинающим трудно читать задания из-за необъясненных терминов и неестественных формулировок. - Что: переработаны вводная и 21 задание, названы темы разделов, объяснены термины и сокращены повторы; согласованы правила учебного текста и ссылки. - Проверка: сохранены 25 блоков SQL и команд и таблицы состава колонок; проверены 153 локальные ссылки и 32 входящие ссылки на разделы; git diff --check прошел.
This commit is contained in:
1 parent
3c3eb825e1
commit
4693bc5af9
24 files changed
+782
-684
No files matched your search
@@ -1,9 +1,9 @@
|
||||
# Разберите сырые события рядом со стендом
|
||||
# Разбор сырых событий
|
||||
|
||||
**Чему учит:** пакетно разобрать сообщения отдельно от живого приема и
|
||||
построчно проверить свой результат по нетронутому ODS.
|
||||
В этой работе вы разберете сохраненные сообщения о событиях в свою таблицу.
|
||||
Каждую полученную строку сравните с результатом обработки в стенде.
|
||||
|
||||
## Сначала прочитать
|
||||
## Подготовка
|
||||
|
||||
- [формат события](../formats/clickstream-event.md);
|
||||
- [справочник хранилища](../architecture/storage.md), разделы про имена
|
||||
@@ -15,26 +15,26 @@
|
||||
Готовый стендовый разбор находится в `sql/ddl/30-ods-views.sql`. Откройте его
|
||||
после собственной сверки: в нем лежит ответ на основную часть задания.
|
||||
|
||||
## Что предстоит сделать
|
||||
## Порядок работы
|
||||
|
||||
Создайте в базе `default` одну свою таблицу годных событий и наполните ее одним
|
||||
пакетным `INSERT SELECT` из `stg.hits_raw_dist`. Работайте с первой нодой под
|
||||
пользователем `etl`.
|
||||
Создайте в базе `default` свою таблицу для правильно заполненных событий.
|
||||
Выберите небольшой набор сообщений из `stg.hits_raw_dist`, разберите их
|
||||
и загрузите в таблицу одним `INSERT SELECT`. Работайте с первым узлом под
|
||||
пользователем `etl`. Затем сравните результат с событиями в ODS.
|
||||
|
||||
Матвью `ods.event_mv` и `ods.event_errors_mv` продолжают принимать живой
|
||||
поток. Оставьте их, STG, ODS, Kafka и даги без изменений. Пересоздание матвью
|
||||
на работающем стенде оставило бы зазор в приеме, а для пакетного опыта оно не
|
||||
нужно.
|
||||
Материализованные представления `ods.event_mv` и `ods.event_errors_mv`
|
||||
продолжают обрабатывать поступающие сообщения. Оставьте их, STG, ODS, Kafka
|
||||
и даги без изменений. Если пересоздать представления на работающем стенде,
|
||||
часть сообщений может остаться без обработки.
|
||||
|
||||
Дайте своей таблице имя, которое не пересечется с чужой работой, и запишите
|
||||
его вместе с запросами. В конце вы удалите только эту таблицу.
|
||||
его вместе с запросами.
|
||||
|
||||
## Выберите сырой срез
|
||||
## Выбор исходных сообщений
|
||||
|
||||
STG хранит строки примерно три дня по `_load_ts`. Эта колонка отмечает время
|
||||
поступления в хранилище. Модельный день события лежит в `EventDate`. Сначала
|
||||
найдите среди недавних данных завершившийся интервал со строками. Посмотрите
|
||||
счет по минутам:
|
||||
поступления в хранилище. Модельный день события лежит в `EventDate`.
|
||||
Посмотрите, сколько строк поступило за каждую из последних минут:
|
||||
|
||||
```sql
|
||||
SELECT
|
||||
@@ -48,7 +48,8 @@ ORDER BY minute DESC;
|
||||
```
|
||||
|
||||
Выберите короткий интервал `[slice_from, slice_to)`, который уже закончился и
|
||||
дает удобное для построчной проверки число строк. Если одна минута велика,
|
||||
дает удобное для построчной проверки число строк. Этот набор строк дальше
|
||||
называется срезом. Если за одну минуту пришло слишком много строк,
|
||||
сузьте ее точными границами `_load_ts`. Если недавних строк нет, откройте
|
||||
Airflow. При включенном `world_live` посмотрите его текущий запуск и дочерний
|
||||
`world_live_day`: дождитесь свежих строк от текущего или следующего планового
|
||||
@@ -64,9 +65,9 @@ Airflow. При включенном `world_live` посмотрите его т
|
||||
|
||||
Во всех дальнейших запросах читайте `stg.hits_raw_dist` только с этими
|
||||
границами. Новые строки с более поздним `_load_ts` в срез не попадут. Повторите
|
||||
опыт до очистки по TTL; после нее прежний срез уже не восстановить из ODS.
|
||||
опыт до удаления исходных строк по TTL; после него прежний срез уже не восстановить из ODS.
|
||||
|
||||
## Соберите собственную таблицу
|
||||
## Таблица для результата
|
||||
|
||||
Таблица содержит все 47 полей источника с именами и типами
|
||||
`ods.event_rep`, а также `_load_ts`. Тип `purchaseRevenue` возьмите из ODS:
|
||||
@@ -75,14 +76,15 @@ Airflow. При включенном `world_live` посмотрите его т
|
||||
Обработайте повторные доставки так же, как ODS. Возьмите
|
||||
`ReplacingMergeTree(_load_ts)`, партицию `EventDate` и полный ключ замены:
|
||||
`(CounterID, EventDate, intHash32(ClientID), WatchID)`. Достаточно одной
|
||||
локальной таблицы: загрузка и сверка в этом опыте выполняются на одной ноде.
|
||||
локальной таблицы: загрузка и сверка в этом опыте выполняются на одном узле.
|
||||
|
||||
`WatchID` — логический идентификатор события, уникальный по
|
||||
[договору генератора](../specs/2026-08-01-generator.md). Физический ключ ODS
|
||||
шире: в `ReplacingMergeTree` версиями считаются строки с одинаковым полным
|
||||
ключом сортировки. Используйте его при загрузке и сверке.
|
||||
`WatchID` обозначает событие и уникален по
|
||||
[правилам генератора](../specs/2026-08-01-generator.md). Но `ReplacingMergeTree`
|
||||
считает версиями строки, у которых совпадает весь ключ сортировки.
|
||||
Поэтому для загрузки и сверки используйте все четыре части ключа ODS,
|
||||
перечисленные выше.
|
||||
|
||||
## Разберите годные строки
|
||||
## Разбор сообщений
|
||||
|
||||
Для каждой строки среза проверьте те же условия приема, что и на стенде:
|
||||
|
||||
@@ -92,8 +94,10 @@ Airflow. При включенном `world_live` посмотрите его т
|
||||
в ожидаемые типы.
|
||||
|
||||
Напишите разбор, который не прерывает пакет из-за одной грязной строки. Сами
|
||||
выберите способ безопасного извлечения пяти опорных полей, форму общих выражений
|
||||
и место фильтра. Остальные поля извлеките по описанию формата.
|
||||
выберите, как извлечь пять перечисленных полей без ошибки запроса, какие
|
||||
выражения вынести в общую часть и где отфильтровать неподходящие строки.
|
||||
Эти пять полей дальше называются опорными. Остальные поля извлеките
|
||||
по описанию формата.
|
||||
|
||||
Перенесите `_load_ts` из STG без пересчета: это время исходной доставки и
|
||||
колонка версии. Время запуска вашего запроса сюда не входит. Вставьте в свою
|
||||
@@ -114,25 +118,25 @@ Airflow. При включенном `world_live` посмотрите его т
|
||||
условия отбора на этом срезе, но не классификатор ошибок. Подкладывать грязные
|
||||
сообщения ради задания не требуется.
|
||||
|
||||
## Приведите стороны к одному зерну
|
||||
## Подготовка к сравнению
|
||||
|
||||
### Учтите повторные доставки
|
||||
### Повторные доставки
|
||||
|
||||
В STG одна строка представляет доставку. Повтор сообщения остается второй
|
||||
строкой. В `ods.event_v` одна строка представляет актуальное событие, потому
|
||||
что представление читает физическую таблицу с `FINAL`.
|
||||
|
||||
Свою сторону тоже читайте с `FINAL`. Ожидаемый счет равен числу различных
|
||||
полных ключей среди годных доставок. Разница между числом доставок и этим
|
||||
счетом показывает повторы в исходном срезе.
|
||||
Свою таблицу тоже читайте с `FINAL`. Число строк должно совпасть с числом
|
||||
различных полных ключей среди годных исходных сообщений. Разница между числом
|
||||
годных доставок и этим числом показывает повторы в исходном срезе.
|
||||
|
||||
### Выберите события ODS для сравнения
|
||||
### События ODS для сравнения
|
||||
|
||||
Теперь нужно найти в ODS события из выбранного среза. Список стройте по
|
||||
исходным доставкам: если взять его из своей таблицы, ошибочно отвергнутое
|
||||
событие исчезнет с обеих сторон сверки и потеря останется незамеченной.
|
||||
|
||||
Снова возьмите весь исходный STG-срез. По координатам Kafka исключите
|
||||
Снова возьмите все выбранные строки STG. По координатам Kafka исключите
|
||||
доставки, которые есть в `ods.event_errors_dist`. У оставшихся извлеките
|
||||
`WatchID` без риска прервать запрос и выберите эти события из свежего
|
||||
`ods.event_v`. Свое условие отбора здесь не применяйте: решение о годности
|
||||
@@ -141,36 +145,38 @@ Airflow. При включенном `world_live` посмотрите его т
|
||||
`WatchID` связывает доставку с событием в ODS. Для удаления повторов и
|
||||
сравнения полей дальше используйте полный физический ключ из схемы таблицы.
|
||||
|
||||
## Сверьте 47 полей
|
||||
## Сверка полей
|
||||
|
||||
Соедините свою таблицу и выбранные строки `ods.event_v` через `FULL OUTER
|
||||
JOIN` по полному ключу замены. Полное соединение сохраняет строки,
|
||||
которых нет на одной из сторон. Разделите результат на три исхода:
|
||||
которых нет на одной из сторон. Найдите три вида расхождений:
|
||||
|
||||
- строка есть только в ODS — вы ее потеряли;
|
||||
- строка есть только у вас — вы приняли лишнее или иначе разобрали часть
|
||||
ключа;
|
||||
- ключ есть с обеих сторон, но различается хотя бы одно из 47 полей.
|
||||
|
||||
ClickHouse по умолчанию заполняет отсутствующую сторону значениями типов.
|
||||
ClickHouse по умолчанию заполняет отсутствующую сторону значениями по умолчанию
|
||||
для соответствующих типов, например нулем или пустой строкой.
|
||||
Включите для сверки `join_use_nulls = 1` или добавьте явный признак присутствия
|
||||
в каждую сторону. Не определяйте отсутствие по значениям полей события.
|
||||
|
||||
Сравните все поля явно, включая массивы, `ecommerce` и точное десятичное
|
||||
`purchaseRevenue`. Удобный вид диагностики выберите сами: важно, чтобы по
|
||||
результату можно было назвать отличающиеся колонки одной строки. Выведите число
|
||||
строк каждого из трех исходов. В верном разборе все три числа равны нулю.
|
||||
`purchaseRevenue`. Выберите, как показать расхождения, чтобы было видно,
|
||||
какие колонки различаются в конкретной строке. Выведите число строк каждого
|
||||
из трех видов расхождений. В верном разборе все три числа равны нулю.
|
||||
|
||||
`_load_ts` не входит в 47 полей источника и не проверяет JSON-разбор.
|
||||
Проверьте его отдельно. Свяжите каждую строку своей таблицы с меткой одной из
|
||||
доставок того же полного ключа в исходном срезе. После `FINAL` остается самая
|
||||
поздняя метка этого ключа внутри среза. С ODS ее не сравнивайте: там может
|
||||
Проверьте его отдельно. Для каждой строки своей таблицы найдите самую позднюю
|
||||
метку доставки того же полного ключа в исходном срезе. Она должна совпасть
|
||||
с `_load_ts`, который вы читаете после `FINAL`. С ODS эту метку не сравнивайте: там может
|
||||
храниться более поздняя доставка того же события из-за границ среза.
|
||||
|
||||
## Расследуйте расхождение
|
||||
## Поиск ошибок разбора
|
||||
|
||||
Если сверка не пуста, возьмите одну строку. Вернитесь к независимому списку
|
||||
ключей и исходному `raw`, выведите рядом координаты Kafka и `_load_ts`.
|
||||
Если нашлись расхождения, выберите одну строку. Найдите ее ключ в списке,
|
||||
который вы получили из STG, и исходное сообщение `raw`. Выведите рядом
|
||||
координаты Kafka и `_load_ts`.
|
||||
|
||||
Для измененной строки назовите поля, которые расходятся, и сравните каждое с
|
||||
сырой строкой. Для пропавшей или лишней строки проверьте пять опорных полей и
|
||||
@@ -178,28 +184,28 @@ ClickHouse по умолчанию заполняет отсутствующую
|
||||
`ods.event_errors_dist`, посмотрите `error_class`.
|
||||
|
||||
Исправьте разбор, удалите и заново создайте только собственную таблицу, затем
|
||||
повторите вставку того же среза. Остановитесь, когда счет исходных доставок по
|
||||
повторите вставку того же среза. Остановитесь, когда число исходных доставок по
|
||||
прежним границам не изменился, число годных и отвергнутых строк в сумме дает
|
||||
число исходных доставок, а построчная сверка пуста.
|
||||
число исходных доставок, а построчная сверка не находит расхождений.
|
||||
|
||||
## Чего не подтверждает сравнение с ODS
|
||||
## Ограничения проверки
|
||||
|
||||
`ods.event_v` — образец для сравнения. Оба разбора могли повторить одну
|
||||
ошибку. Для пяти опорных полей у вас есть проверка преобразования в нужный тип
|
||||
и исходный JSON. Остальные 42 поля можно вручную сопоставить с `raw`, однако
|
||||
второго независимого источника для всех значений на стенде нет.
|
||||
|
||||
## Уберите свой объект
|
||||
## Сохранение результата и удаление таблицы
|
||||
|
||||
Сохраните DDL таблицы, запрос разбора, границы среза, число исходных, годных
|
||||
и отвергнутых строк и итоговую сверку без расхождений. Запишите также, чего
|
||||
не подтверждает сравнение с ODS и как TTL ограничивает время для повторения
|
||||
опыта. Затем удалите свою таблицу командой `DROP TABLE` с ее точным именем.
|
||||
Убедитесь через `system.tables`, что объекта больше нет.
|
||||
Прикладные строки стенда вы не меняли, поэтому удалять данные из STG, ODS или
|
||||
Данные стенда вы не меняли, поэтому удалять строки из STG, ODS или
|
||||
Kafka после опыта не нужно.
|
||||
|
||||
## Что должно получиться
|
||||
## Результат работы
|
||||
|
||||
- [ ] у вас сохранены границы выбранного среза, число строк и координаты
|
||||
доставки по партициям Kafka;
|
||||
|
||||
Reference in new issue
Block a user