Перейти к основному содержимому

Настройка базы данных, схемы и псевдонима моделей dbt

Обновлен
Advanced
Menu

    Введение

    Это руководство объясняет, как настроить соглашения об именовании схемы, базы данных и псевдонима в 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>.
    • Имя объекта зависит от того, определен ли псевдоним для модели:

      • Если псевдоним не определен, объект будет создан с тем же именем, что и модель, без .sql или .py в конце.
        • Например, предположим, что у нас есть модель, где файл sql называется fct_orders_complete.sql, пользовательская схема — marketing, и пользовательский псевдоним не настроен. Результирующая модель будет создана в dbt_myschema_marketing.fct_orders_complete в среде разработки.
      • Если псевдоним определен, объект будет создан с настроенным псевдонимом.
      • Например, предположим, что у нас есть модель, где файл 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 и других.
    • Или в качестве альтернативы переменным окружения вы можете использовать target.name. Для получения дополнительной информации вы можете обратиться к О переменных target.
    Пользовательские переменные окружения схемы target name.Пользовательские переменные окружения схемы target name.

    Чтобы позволить имени базы данных/схемы/объекта зависеть от текущей ветки, вы можете использовать встроенную переменную окружения DBT_CLOUD_GIT_BRANCH в dbt Cloud специальные переменные окружения.

    Примеры использования

    Вот некоторые типичные примеры, с которыми мы сталкивались у пользователей dbt, использующих эти 3 макроса и различную логику.

    примечание

    Обратите внимание, что следующие примеры не являются исчерпывающими и не охватывают все доступные варианты. Эти примеры предназначены для того, чтобы служить шаблонами для разработки вашего собственного поведения.

    1. Пользовательские схемы без объединения целевой схемы в производстве

    Наиболее распространенный случай использования — это использование пользовательской схемы без объединения ее с именем схемы по умолчанию в производственной среде.

    Для этого вы можете создать новый файл с именем generate_schema_name.sql в вашей папке макросов со следующим кодом:

    macros/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 из разных контекстов.

    КонтекстЦелевая база данныхЦелевая схемаРезультирующий объект
    Разработчик 1devdbt_dev1dev.dbt_dev1_marketing.my_model
    Разработчик 2devdbt_dev2dev.dbt_dev2_marketing.my_model
    CI PR 123cidbt_pr_123ci.dbt_pr_123_marketing.my_model
    CI PR 234cidbt_pr_234ci.dbt_pr_234_marketing.my_model
    Производствоprodanalyticsprod.marketing.my_model
    примечание

    Мы добавили логику для проверки, происходит ли текущий запуск dbt в производственной среде или нет. Это важно, и мы объясняем, почему в разделе Чего не следует делать.

    2. Статические схемы: добавление идентификаторов разработчиков к таблицам

    Иногда мы сталкиваемся с ситуациями, когда политика безопасности организации не позволяет разработчикам создавать схемы, и все разработчики должны разрабатывать в одной схеме.

    В этом случае мы можем:

    • Создать новый файл с именем generate_schema_name.sql в вашей папке макросов со следующим кодом:

    • Изменить generate_schema_name(), чтобы использовать одну схему для всех разработчиков, даже если установлена пользовательская схема.

    • Обновить generate_alias_name(), чтобы добавить псевдоним разработчика и пользовательскую схему в начало имени таблицы в среде разработки.

      • Этот метод не идеален, так как может привести к длинным именам таблиц, но он позволит разработчикам увидеть, в какой схеме модель будет создана в производственной среде.
    macros/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') != 'CI' -%}

    {{ custom_schema_name | trim }}

    {%- else -%}

    {{ default_schema }}_{{ custom_schema_name | trim }}

    {%- endif -%}

    {%- endmacro %}

    macros/generate_alias_name.sql

    {% 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 из разных контекстов.

    КонтекстЦелевая база данныхЦелевая схемаРезультирующий объект
    Разработчик 1devdbt_dev1dev.marketing.dbt_dev1_my_model
    Разработчик 2devdbt_dev2dev.marketing.dbt_dev2_my_model
    CI PR 123cidbt_pr_123ci.dbt_pr_123_marketing.my_model
    CI PR 234cidbt_pr_234ci.dbt_pr_234_marketing.my_model
    Производствоprodanalyticsprod.marketing.my_model

    3. Использование имени ветки в качестве префикса схемы

    Для команд, которые предпочитают изолировать работу на основе ветки функции, вы можете воспользоваться специальной переменной окружения DBT_CLOUD_GIT_BRANCH. Обратите внимание, что разработчики будут записывать в одну и ту же схему, когда они находятся в одной и той же ветке функции.

    примечание

    Переменная DBT_CLOUD_GIT_BRANCH доступна только в IDE dbt Cloud и недоступна в Cloud CLI.

    Мы также видели, что некоторые организации предпочитают организовывать свои базы данных разработки по имени ветки. Это требует реализации аналогичной логики в generate_database_name() вместо макроса generate_schema_name(). По умолчанию dbt не будет автоматически создавать базы данных.

    Обратитесь к разделу Советы и рекомендации, чтобы узнать больше.

    macros/generate_schema_name.sql

    {% 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 из разных контекстов.

    КонтекстВеткаЦелевая база данныхЦелевая схемаРезультирующий объект
    Разработчик 1featureABCdevdbt_dev1dev.featureABC_marketing.my_model
    Разработчик 2featureABCdevdbt_dev2dev.featureABC_marketing.my_model
    Разработчик 1feature123devdbt_dev1dev.feature123_marketing.my_model
    CI PR 123cidbt_pr_123ci.dbt_pr_123_marketing.my_model
    CI PR 234cidbt_pr_234ci.dbt_pr_234_marketing.my_model
    Производствоprodanalyticsprod.marketing.my_model

    Когда разработчик 1 и разработчик 2 находятся в одной и той же ветке, они будут генерировать один и тот же объект в хранилище данных. Это не должно быть проблемой, так как нахождение в одной и той же ветке означает, что код модели будет одинаковым для обоих разработчиков.

    4. Использование статической схемы для CI

    Некоторые организации предпочитают записывать свои CI задачи в одну схему с идентификатором PR, добавленным в начало имени таблицы. Важно отметить, что это приведет к длинным именам таблиц.

    Для этого вы можете создать новый файл с именем generate_schema_name.sql в вашей папке макросов со следующим кодом:

    macros/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 %}

    macros/generate_alias_name.sql

    {% 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 из разных контекстов.

    КонтекстЦелевая база данныхЦелевая схемаРезультирующий объект
    Разработчик 1devdbt_dev1dev.dbt_dev1_marketing.my_model
    Разработчик 2devdbt_dev2dev.dbt_dev2_marketing.my_model
    CI PR 123cidbt_pr_123ci.ci_schema.dbt_pr_123_marketing_my_model
    CI PR 234cidbt_pr_234ci.ci_schema.dbt_pr_234_marketing_my_model
    Производствоprodanalyticsprod.marketing.my_model

    Чего не следует делать

    Этот раздел предоставит обзор того, чего пользователям следует избегать при настройке своих схем и псевдонимов из-за проблем, которые могут возникнуть.

    Обновление generate_schema_name() для использования только пользовательской схемы

    Некоторые предпочитают использовать только пользовательскую схему, когда она установлена, вместо объединения схемы по умолчанию с пользовательской, как это происходит в стандартном поведении.

    Проблема

    При изменении стандартного макроса для generate_schema_name() это может привести к созданию этой новой версии.

    macros/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 }}

    {%- else -%}
    # Следующее неверно, так как опущено {{ default_schema }} перед {{ custom_schema_name | trim }}.
    {{ custom_schema_name | trim }}

    {%- endif -%}

    {%- endmacro %}

    Хотя это может дать ожидаемый результат для производства, где используется выделенная база данных, это приведет к конфликтам везде, где люди делятся базой данных.

    Рассмотрим пример модели с именем my_model с пользовательской схемой marketing.

    КонтекстЦелевая база данныхЦелевая схемаРезультирующий объект
    Производствоprodanalyticsprod.marketing.my_model
    Разработчик 1devdbt_dev1dev.marketing.my_model
    Разработчик 2devdbt_dev2dev.marketing.my_model
    CI PR 123cidbt_pr_123ci.marketing.my_model
    CI PR 234cidbt_pr_234ci.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, база данных/схема/псевдоним могут быть разрешены неправильно. В качестве альтернативы, значения переменных окружения наследуются задачами в их соответствующей среде. Значения переменных окружения также могут быть переопределены в задачах, если это необходимо.
    Настройка псевдонима схемы env var.Настройка псевдонима схемы env var.
    • target.name требует, чтобы каждый разработчик вводил одно и то же значение (часто 'dev') в разделе имени цели своих учетных данных проекта разработки. Если у разработчика не установлено соответствующее значение имени цели, его база данных/схема/псевдоним могут быть разрешены неправильно.
    Учетные данные разработки.Учетные данные разработки.

    Всегда применять пользовательские схемы

    Некоторые пользователи предпочитают применять пользовательские схемы ко всем объектам в своих проектах. Это позволяет избежать записи в непреднамеренные "стандартные" местоположения. Вы можете добавить эту логику в ваш макрос generate_schema_name(), чтобы вызывать ошибку компиляции, если для объекта не определена пользовательская схема.

    macros/generate_schema_name.sql

    {% 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 -%}

    0