Условие
Команда жалуется: «отчёты врут». Иногда строки дублируются, иногда исчезают, иногда внезапно появляются NULL в важных колонках. Как вы построите систему data quality? Какие проверки сделаете обязательными, какие — мониторингом?
Решение
Подход
Шесть измерений качества данных.
- Accuracy — данные соответствуют реальности.
- Completeness — нет ли пропусков; все ли ожидаемые строки пришли.
- Consistency — одинаковые сущности в разных таблицах согласованы.
- Uniqueness — нет дубликатов по бизнес-ключу.
- Validity — формат правильный, значения в допустимом диапазоне.
- Timeliness — данные пришли вовремя.
Каркас проверок.
| Уровень | Когда падает | Что делать |
|---|---|---|
| Hard tests (blocking) | Уникальность бизнес-ключа, NOT NULL обязательных колонок, FK-целостность | Остановить пайплайн, страница on-call |
| Soft tests (warning) | Аномальное число строк, распределение значений сместилось | Алерт в Slack, продолжаем pipeline |
| Statistical monitoring | Дрейф распределений признаков | Дашборд + еженедельный review |
Инструменты.
- dbt tests:
unique,not_null,accepted_values,relationships, кастомные SQL-тесты. - Great Expectations: декларативные проверки, поддержка профилирования.
- Soda Core / Monte Carlo / Bigeye: SaaS / open-source observability data-стека.
- Кастомные SQL-checks через Airflow Sensor / Check Operator.
Пример dbt-тестов
# schema.yml
version: 2
models:
- name: fct_orders
columns:
- name: order_id
tests:
- unique
- not_null
- name: customer_id
tests:
- not_null
- relationships:
to: ref('dim_customers')
field: customer_id
- name: status
tests:
- accepted_values:
values: ['new', 'paid', 'shipped', 'cancelled']
- name: amount
tests:
- not_null
- dbt_utils.expression_is_true:
expression: ">= 0"Аномалии в объёме строк
-- Macro-проверка через dbt-utils
WITH today AS (
SELECT COUNT(*) AS n FROM fct_orders WHERE order_date = CURRENT_DATE - 1
),
last7d AS (
SELECT AVG(daily_count)::int AS avg_n, STDDEV(daily_count)::int AS std_n
FROM (
SELECT order_date, COUNT(*) AS daily_count
FROM fct_orders
WHERE order_date BETWEEN CURRENT_DATE - 8 AND CURRENT_DATE - 1
GROUP BY order_date
) t
)
-- Тест проходит, если сегодняшний объём в пределах 3 SD от 7-дневного среднего
SELECT 1 AS test_failure
FROM today, last7d
WHERE ABS(today.n - last7d.avg_n) > 3 * last7d.std_n;Принципы.
- Тесты на бизнес-ключи обязательны в каждой таблице.
unique+not_nullнаorder_idловит 80% инцидентов. - Тесты в CI. Любое изменение dbt-модели запускает
dbt testв pull request — нельзя смержить, если падает. - Алерты привязаны к owner-у. Когда падает тест на
fct_orders, пишет на канал команды orders, а не «всех data engineers». - Reduce alert fatigue. Шумные алерты игнорируют. Тесты с высоким FPR — переводить в дашборд, а не в Slack.
- Контракт с upstream. Если SaaS-источник внезапно поменял схему, лучше узнать на стадии extract, а не в марте. Schema validation + change log от вендора.
Подводные камни
- «Просто
uniqueна бизнес-ключе» не работает, если бизнес-ключ составной (order_id+version). dbt-тестuniqueподдерживает только одну колонку из коробки; используйтеdbt_utils.unique_combination_of_columns. - Тесты только в проде. Без CI меняешь модель, мержишь, ломаешь — узнаёшь от бизнеса. Тесты должны быть и в PR.
- Игнорировать warnings. Soft-тесты превращаются в noise; либо переводите в hard, либо инвестируйте в их разбор.
- Тестов нет на raw слой. Если raw грязный, mart-уровень не спасёт. Тестируйте сразу после load.
Эталонный ответ
Шесть измерений (accuracy / completeness / consistency / uniqueness / validity / timeliness). Hard-тесты блокируют pipeline (unique бизнес-ключа, NOT NULL обязательных полей, FK), soft-тесты алертят (аномалии объёмов и распределений). Инструменты: dbt tests + dbt-utils, Great Expectations или Soda. Тесты обязательны в CI на pull request. Алерты адресуются owner-командам, не общему каналу.