Настройка базы данных, схемы и псевдонима моделей dbt
Введение
Это руководство объясняет, как настроить соглашения об именовании схемы, базы данных и псевдонима в dbt, чтобы они соответствовали требованиям управления и проектирования вашего хранилища данных.
Когда мы разрабатываем модели dbt и выполняем определенные команды (такие как dbt run
или dbt build
), объекты (например, таблицы и представления) создаются в хранилище данных на основе этих соглашений об именовании.
Разные хранилища данных имеют разные названия для логических баз данных. Информация в этом документе охватывает "базы данных" в Snowflake, Redshift и Postgres; "проекты" в BigQuery; и "каталоги" в Databricks Unity Catalog.
Следующее поведение является стандартным для dbt:
-
База данных, в которой создается объект, определяется базой данных, настроенной на уровне окружения в dbt Cloud или в файле
profiles.yml
в dbt Core. -
Схема зависит от того, определили ли вы пользовательскую схему для модели:
- Если пользовательская схема не определена, dbt создает объект в схеме по умолчанию. В dbt Cloud это обычно
dbt_username
для разработки и схема по умолчанию для сред развертывания. В dbt Core используется схема, указанная в файлеprofiles.yml
. - Если вы определяете пользовательскую схему, dbt объединяет ранее упомянутую схему с пользовательской.
- Например, если настроенная схема
dbt_myschema
, а пользовательская —marketing
, объекты будут созданы подdbt_myschema_marketing
. - Обратите внимание, что для автоматизированных CI задач имя схемы выводится из номера задачи и номера PR:
dbt_cloud_pr_<job_id>_<pr_id>
.
- Если пользовательская схема не определена, dbt создает объект в схеме по умолчанию. В dbt Cloud это обычно
-
Имя объекта зависит от того, определен ли псевдоним для модели:
- Если псевдоним не определен, объект будет создан с тем же именем, что и модель, без
.sql
или.py
в конце.- Например, предположим, что у нас есть модель, где файл sql называется
fct_orders_complete.sql
, пользовательская схема —marketing
, и пользовательский псевдоним не настроен. Результирующая модель будет создана вdbt_myschema_marketing.fct_orders_complete
в среде разработки.
- Например, предположим, что у нас есть модель, где файл sql называется
- Если псевдоним определен, объект будет создан с настроенным псевдонимом.
- Например, предположим, что у нас есть модель, где файл sql называется
fct_orders_complete.sql
, пользовательская схема —marketing
, и псевдоним настроен какfct_orders
. Результирующая модель будет создана вdbt_myschema_marketing.fct_orders
.
- Если псевдоним не определен, объект будет создан с тем же именем, что и модель, без
Эти стандартные правила являются отличной отправной точкой, и многие организации предпочитают придерживаться их без необходимости в настройке.
По умолчанию разработчики могут работать в своих изолированных схемах (песочницах), не перезаписывая работу друг друга — даже если они работают над одними и теми же таблицами.
Как настроить это поведение
Хотя стандартное поведение удовлетворит потребности большинства организаций, бывают случаи, когда этот подход не сработает.
Например, dbt ожидает, что у него есть разрешение на создание схем по мере необходимости (и мы рекомендуем, чтобы пользователи, запускающие dbt, имели такую возможность), но это может быть не разрешено в вашей компании.
Или, в зависимости от того, как вы спроектировали свое хранилище, вы можете захотеть минимизировать количество схем в вашей среде разработки (и избежать разрастания схем, не создавая комбинацию всех схем разработчиков и пользовательских схем).
Кроме того, вы можете даже захотеть, чтобы ваши схемы разработки назывались в честь веток функций, а не имени разработчика.
По этой причине dbt предлагает три макроса для настройки того, какие объекты создаются в хранилище данных:
Переопределяя один или несколько из этих макросов, мы можем настроить, где создаются объекты dbt в хранилище данных, и согласовать это с любыми существующими требованиями.
Модели, запускаемые из двух разных контекстов, должны приводить к уникальным объектам в хранилище данных. Например, разработчик по имени Сьюзи работает над улучшениями fct_player_stats
, но Даррен разрабатывает тот же самый объект.
Чтобы предотвратить перезапись работы друг друга, и Сьюзи, и Даррен должны иметь свои уникальные версии fct_player_stats
в среде разработки.
Кроме того, промежуточная версия fct_player_stats
должна существовать в ун икальном месте, отдельно от версий разработки и производства.
Мы часто используем следующее при настройке этих макросов:
- В dbt Cloud мы рекомендуем использовать переменные окружения для определения, где происходит вызов dbt (dev/stg/prod).
- Они могут быть установлены на уровне окружения, и все задачи автоматически унаследуют значения по умолчанию. Мы добавим логику jinja (
if/else/endif
), чтобы определить, происходит ли запуск в dev, prod, CI и других.
- Они могут быть установлены на уровне окружения, и все задачи автоматически унаследуют значения по умолчанию. Мы добавим логику jinja (
- Или в качестве альтернативы переменным окружения вы можете использовать
target.name
. Для получения дополнительной информации вы можете обратиться к О переменных target.
Чтобы позволить имени базы данных/схемы/объекта зависеть от текущей ветки, вы можете использовать встроенную переменную окружения DBT_CLOUD_GIT_BRANCH
в dbt Cloud специальные переменные окружения.
Примеры использования
Вот некоторые типичные примеры, с которыми мы сталкивались у пользователей dbt, использующих эти 3 макроса и различную логику.
Обратите внимание, что следующие примеры не являются исчерпывающими и не охватывают все доступные варианты. Эти примеры предназначены для того, чтобы служить шаблонами для разработки вашего собственного поведения.
- Использование пользовательской схемы без объединения целевой схемы в производстве
- Добавление идентификаторов разработчиков к таблицам
- Использование имени ветки в качестве префикса схемы
- Использование статической схемы для CI
1. Пользовательские схемы без объединения целевой схемы в производстве
Наиболее распространенный случай использования — это использование пользовательской схемы без объединения ее с именем схемы по умолчанию в производственной среде.
Для этого вы можете создать новый файл с именем generate_schema_name.sql
в вашей папке макросов со следующим кодом:
{% macro generate_schema_name(custom_schema_name, node) -%}
{%- set default_schema = target.schema -%}
{%- if custom_schema_name is none -%}
{{ default_schema }}
{%- elif env_var('DBT_ENV_TYPE','DEV') == 'PROD' -%}
{{ custom_schema_name | trim }}
{%- else -%}
{{ default_schema }}_{{ custom_schema_name | trim }}
{%- endif -%}
{%- endmacro %}
Это создаст следующие выходные данные для модели с именем my_model
с пользовательской схемой marketing
, предотвращая любое пересечение объектов между запусками dbt из разных контекстов.
Контекст | Целевая база данных | Целевая схема | Результирующий объект |
---|---|---|---|
Разработчик 1 | dev | dbt_dev1 | dev.dbt_dev1_marketing.my_model |
Разработчик 2 | dev | dbt_dev2 | dev.dbt_dev2_marketing.my_model |
CI PR 123 | ci | dbt_pr_123 | ci.dbt_pr_123_marketing.my_model |
CI PR 234 | ci | dbt_pr_234 | ci.dbt_pr_234_marketing.my_model |
Производство | prod | analytics | prod.marketing.my_model |
Мы добавили логику для проверки, происходит ли текущий запуск dbt в производственной среде или нет. Это важно, и мы объясняем, почему в разделе Чего не следует делать.
2. Статические схемы: добавление идентификаторов разработчиков к таблицам
Иногда мы сталкиваемся с ситуациями, когда политика безопасности организации не позволяет разработчикам создавать схем ы, и все разработчики должны разрабатывать в одной схеме.
В этом случае мы можем:
-
Создать новый файл с именем generate_schema_name.sql в вашей папке макросов со следующим кодом:
-
Изменить
generate_schema_name()
, чтобы использовать одну схему для всех разработчиков, даже если установлена пользовательская схема. -
Обновить
generate_alias_name()
, чтобы добавить псевдоним разработчика и пользовательскую схему в начало имени таблицы в среде разработки.- Этот метод не идеален, так как может привести к длинным именам таблиц, но он позволит разработчикам увидеть, в какой схеме модель будет создана в производственной среде.
{% macro generate_schema_name(custom_schema_name, node) -%}
{%- set default_schema = target.schema -%}
{%- if custom_schema_name is none -%}
{{ default_schema }}
{%- elif env_var('DBT_ENV_TYPE','DEV') != 'CI' -%}
{{ custom_schema_name | trim }}
{%- else -%}
{{ default_schema }}_{{ custom_schema_name | trim }}
{%- endif -%}
{%- endmacro %}
{% macro generate_alias_name(custom_alias_name=none, node=none) -%}
{%- if env_var('DBT_ENV_TYPE','DEV') == 'DEV' -%}
{%- if custom_alias_name -%}
{{ target.schema }}__{{ custom_alias_name | trim }}
{%- elif node.version -%}
{{ target.schema }}__{{ node.name ~ "_v" ~ (node.version | replace(".", "_")) }}
{%- else -%}
{{ target.schema }}__{{ node.name }}
{%- endif -%}
{%- else -%}
{%- if custom_alias_name -%}
{{ custom_alias_name | trim }}
{%- elif node.version -%}
{{ return(node.name ~ "_v" ~ (node.version | replace(".", "_"))) }}
{%- else -%}
{{ node.name }}
{%- endif -%}
{%- endif -%}
{%- endmacro %}
Это создаст следующие выходные данные для модели с именем my_model
с пользовательской схемой marketing
, предотвращая любое пересечение объектов между запусками dbt из разных контекстов.
Контекст | Целевая база данных | Целевая схема | Результирующий объект |
---|---|---|---|
Разработчик 1 | dev | dbt_dev1 | dev.marketing.dbt_dev1_my_model |
Разработчик 2 | dev | dbt_dev2 | dev.marketing.dbt_dev2_my_model |
CI PR 123 | ci | dbt_pr_123 | ci.dbt_pr_123_marketing.my_model |
CI PR 234 | ci | dbt_pr_234 | ci.dbt_pr_234_marketing.my_model |
Производство | prod | analytics | prod.marketing.my_model |
3. Использование имени ветки в качестве префикса схемы
Для команд, которые предпочитают изолировать работу на основе ветки функции, вы можете воспользоваться специальной переменной окружения DBT_CLOUD_GIT_BRANCH
. Обратите внимание, что разработчики будут записывать в одну и ту же схему, когда они находятся в одн ой и той же ветке функции.
Переменная DBT_CLOUD_GIT_BRANCH
доступна только в IDE dbt Cloud и недоступна в Cloud CLI.
Мы также видели, что некоторые организации предпочитают организовывать свои базы данных разработки по имени ветки. Это требует реализации аналогичной логики в generate_database_name()
вместо макроса generate_schema_name()
. По умолчанию dbt не будет автоматически создавать базы данных.
Обратитесь к разделу Советы и рекомендации, чтобы узнать больше.
{% macro generate_schema_name(custom_schema_name, node) -%}
{%- set default_schema = target.schema -%}
{%- if env_var('DBT_ENV_TYPE','DEV') == 'DEV' -%}
{#- мы заменяем символы, не разрешенные в именах схем, на "_" -#}
{%- set re = modules.re -%}
{%- set cleaned_branch = re.sub("\W", "_", env_var('DBT_CLOUD_GIT_BRANCH')) -%}
{%- if custom_schema_name is none -%}
{{ cleaned_branch }}
{%- else -%}
{{ cleaned_branch }}_{{ custom_schema_name | trim }}
{%- endif -%}
{%- else -%}
{{ default_schema }}_{{ custom_schema_name | trim }}
{%- endif -%}
{%- endmacro %}
Это создаст следующие выходные данные для модели с именем my_model
с пользовательской схемой marketing
, предотвращая любое пересечение объектов между запусками dbt из разных контекстов.
Контекст | Ветка | Целевая база данных | Целевая схема | Результирующий объект |
---|---|---|---|---|
Разработчик 1 | featureABC | dev | dbt_dev1 | dev.featureABC_marketing.my_model |
Разработчик 2 | featureABC | dev | dbt_dev2 | dev.featureABC_marketing.my_model |
Разработчик 1 | feature123 | dev | dbt_dev1 | dev.feature123_marketing.my_model |
CI PR 123 | ci | dbt_pr_123 | ci.dbt_pr_123_marketing.my_model | |
CI PR 234 | ci | dbt_pr_234 | ci.dbt_pr_234_marketing.my_model | |
Производство | prod | analytics | prod.marketing.my_model |
Когда разработчик 1 и разработчик 2 находятся в одной и той же ветке, они будут генерировать один и тот же объект в хранилище данных. Это не должно быть проблемой, так как нахождение в одной и той же ветке означает, что код модели будет одинаковым для обоих разработчиков.
4. Использование статической схемы для CI
Некоторые организации предпочитают записывать свои CI задачи в одну схему с идентификатором PR, добавленным в начало имени таблицы. Важно отметить, что это приведет к длинным именам таблиц.
Для этого вы можете создать новый файл с именем generate_schema_name.sql
в вашей папке макросов со следующим кодом:
{% macro generate_schema_name(custom_schema_name=none, node=none) -%}
{%- set default_schema = target.schema -%}
{# Если задача CI не существует в своей собственной среде, используйте переменную target.name внутри з адачи #}
{# {%- if target.name == 'CI' -%} #}
{%- if env_var('DBT_ENV_TYPE','DEV') == 'CI' -%}
ci_schema
{%- elif custom_schema_name is none -%}
{{ default_schema }}
{%- else -%}
{{ default_schema }}_{{ custom_schema_name | trim }}
{%- endif -%}
{%- endmacro %}
{% macro generate_alias_name(custom_alias_name=none, node=none) -%}
{# Если задача CI не существует в своей собственной среде, используйте переменную target.name внутри задачи #}
{# {%- if target.name == 'CI' -%} #}
{%- if env_var('DBT_ENV_TYPE','DEV') == 'CI' -%}
{%- if custom_alias_name -%}
{{ target.schema }}__{{ node.config.schema }}__{{ custom_alias_name | trim }}
{%- elif node.version -%}
{{ target.schema }}__{{ node.config.schema }}__{{ node.name ~ "_v" ~ (node.version | replace(".", "_")) }}
{%- else -%}
{{ target.schema }}__{{ node.config.schema }}__{{ node.name }}
{%- endif -%}
{%- else -%}
{%- if custom_alias_name -%}
{{ custom_alias_name | trim }}
{%- elif node.version -%}
{{ return(node.name ~ "_v" ~ (node.version | replace(".", "_"))) }}
{%- else -%}
{{ node.name }}
{%- endif -%}
{%- endif -%}
{%- endmacro %}
Это создаст следующие выходные данные для модели с именем my_model
с пользовательской схемой marketing
, предотвращая любое пересечение объектов между запусками dbt из разных контекстов.
Контекст | Целевая база данных | Целевая схема | Результирующий объект |
---|---|---|---|
Разработчик 1 | dev | dbt_dev1 | dev.dbt_dev1_marketing.my_model |
Разработчик 2 | dev | dbt_dev2 | dev.dbt_dev2_marketing.my_model |
CI PR 123 | ci | dbt_pr_123 | ci.ci_schema.dbt_pr_123_marketing_my_model |
CI PR 234 | ci | dbt_pr_234 | ci.ci_schema.dbt_pr_234_marketing_my_model |
Производство | prod | analytics | prod.marketing.my_model |
Чего не следует делать
Этот раздел предоставит обзор того, чего пользователям следует избегать при настройке своих схем и псевдонимов из-за проблем, которые могут возникнуть.
Обновление generate_schema_name() для использования только пользовательской схемы
Некоторые предпочитают использовать только пользовательскую схему, когда она установлена, вместо объединения схемы по умолчанию с пользовательской, как это происходит в стандартном поведении.
Проблема
При изменении стандартного макроса для generate_schema_name()
это может привести к созданию этой новой версии.
{% macro generate_schema_name(custom_schema_name, node) -%}
{%- set default_schema = target.schema -%}
{%- if custom_schema_name is none -%}
{{ default_schema }}
{%- else -%}
# Следующее неверно, так как опущено {{ default_schema }} перед {{ custom_schema_name | trim }}.
{{ custom_schema_name | trim }}
{%- endif -%}
{%- endmacro %}
Хотя это может дать ожидаемый результат для производства, где используется выделенная база данных, это приведет к конфликтам везде, где люди делятся базой данных.
Рассмотрим пример модели с именем my_model
с пользовательской схемой marketing
.
Контекст | Целевая база данных | Целевая схема | Результирующий объект |
---|---|---|---|
Производство | prod | analytics | prod.marketing.my_model |
Разработчик 1 | dev | dbt_dev1 | dev.marketing.my_model |
Разработчик 2 | dev | dbt_dev2 | dev.marketing.my_model |
CI PR 123 | ci | dbt_pr_123 | ci.marketing.my_model |
CI PR 234 | ci | dbt_pr_234 | ci.marketing.my_model |
Мы видим, что и разработчик 1, и разработчик 2 получают один и тот же объект для my_model
. Это означает, что если они оба работают над этой моделью одновременно, будет невозможно узнать, какая версия в данный момент находится в хранилище данных — от разработчика 1 или разработчика 2.
Аналогично, разные PR приведут к созданию одного и того же объекта в хранилище данных. Если разные PR открыты одновременно и изменяют одни и те же модели, очень вероятно, что возникнут проблемы, замедляющие весь процесс разработки и продвижения кода.
Решение
Как описано в предыдущем примере, обновите макрос, чтобы проверить, выполняется ли dbt в производственной среде. Только в производственной среде мы должны удалить объедине ние и использовать только пользовательскую схему.
Советы и рекомендации
Этот раздел предоставит полезные советы о том, как правильно настроить ваши макросы generate_database_name()
и generate_alias_name()
.
Создание несуществующих баз данных из dbt
dbt автоматически попытается создать схему, если она не существует и если в ней нужно создать объект, но он не будет автоматически пытаться создать базу данных, которая не существует.
Таким образом, если ваша конфигурация generate_database_name()
указывает на разные базы данных, которые могут не существовать, dbt завершится с ошибкой, если вы выполните простую команду dbt build
.
Тем не менее, это все еще можно сделать в dbt, создав несколько макросов, которые будут проверять, существует ли база данных, и если нет, dbt создаст ее. Вы можете вызвать эти макросы либо в шаге dbt run-operation ...
в ваших задачах, либо в качестве хука on-run-start
.
Предположение контекста с использованием переменных окружения вместо target.name
Мы предпочитаем использовать переменные окружения вместо target.name
. Для дальнейшего чтения ознакомьтесь с (О переменных target), чтобы расшифровать контекст вызова dbt.
target.name
не может быть установлен на уровне окружен ия. Поэтому каждая задача в среде должна явно указывать переопределениеtarget.name
. Если задача не имеет установленного соответствующего значенияtarget.name
, база данных/схема/псевдоним могут быть разрешены неправильно. В качестве альтернативы, значения переменных окружения наследуются задачами в их соответствующей среде. Значения переменных окружения также могут быть переопределены в задачах, если это необходимо.
target.name
требует, чтобы каждый разработчик вводил одно и то же значение (часто 'dev') в разделе имени цели своих учетных данных проекта разработки. Если у разработчика не установлено соответствующее значение имени цели, его база данных/схема/псевдоним могут быть разрешены неправильно.
Всегда применять пользовательские схемы
Некоторые пользователи предпочитают применять пользовательские схемы ко всем объектам в своих проектах. Это позволяет избежать записи в непреднамеренные "стандартные" местоположения. Вы можете добавить эту логику в ваш макрос generate_schema_name()
, чтобы вызывать ошибку компиляции, если для объекта не определена пользовательская схема.
{% macro generate_schema_name(custom_schema_name, node) -%}
{%- set default_schema = target.schema -%}
{%- if custom_schema_name is none and node.resource_type == 'model' -%}
{{ exceptions.raise_compiler_error("Ошибка: не определена пользовательская схема для модели " ~ node.name ) }}
{%- endif -%}