BigQuery 구성
BigQuery 구성 (BigQuery configurations)
이 문서는 dbt에서 BigQuery 어댑터를 사용할 때 모델에 적용할 수 있는 다양한 리소스 구성(config)에 대해 설명해요. 파티셔닝과 클러스터링, 예약(reservation), KMS 암호화, 라벨과 태그, 병합 전략, 테이블 만료, 승인된 뷰, 구체화된 뷰, Python 모델 구성 등을 강사 목소리로 차근차근 살펴볼게요. 각 설정이 실제로 어떻게 BigQuery에 반영되는지 코드 예시와 함께 익힐 수 있어요.
출처: 문서
본문
구성에서 프로젝트와 데이터셋 사용하기
schema는 BigQuery의dataset개념과 서로 바꿔 쓸 수 있어요.database는 BigQuery의project개념과 서로 바꿔 쓸 수 있어요.
참고 문서에서는 database 대신 project를 선언할 수 있어요. 이렇게 하면 여러 BigQuery 프로젝트에서 읽고 쓸 수 있게 됩니다. dataset도 마찬가지예요.
테이블 파티셔닝과 클러스터링 사용하기
파티션 절(Partition clause)
BigQuery는 partition by 절을 사용해 컬럼이나 표현식으로 테이블을 쉽게 파티셔닝할 수 있도록 지원해요. 이 옵션은 대형 테이블을 조회할 때 지연 시간과 비용을 줄여줄 수 있어요. 단, 파티션 프루닝(partition pruning)은 파티션을 리터럴 값으로 필터링할 때만 작동하기 때문에, 서브쿼리로 파티션을 선택하면 성능이 개선되지 않는다는 점을 기억하세요.
partition_by 구성은 다음과 같은 형식의 사전(dictionary)으로 제공할 수 있어요.
{
"field": "<field name>",
"data_type": "<timestamp | date | datetime | int64>",
"granularity": "<hour | day | month | year>"
# data_type이 "int64"인 경우에만 필요
"range": {
"start": <int>,
"end": <int>,
"interval": <int>
}
}
날짜 또는 타임스탬프로 파티셔닝
datetime 또는 timestamp 컬럼으로 데이터를 파티셔닝하면 hour, day, month, year 단위의 파티션을 만들 수 있어요. date 컬럼은 day, month, year 단위를 지원합니다. 모든 컬럼 유형에서 일별 파티셔닝이 기본값이에요.
data_type이 date로 지정되고 granularity가 day라면, dbt는 테이블 파티셔닝을 구성할 때 해당 필드를 그대로 제공합니다.
bigquery_table.sql
{{ config(
materialized='table',
partition_by={
"field": "created_at",
"data_type": "timestamp",
"granularity": "day"
}
)}}
select
user_id,
event_name,
created_at
from {{ ref('events') }}
컴파일된 코드:
create table `projectname`.`analytics`.`bigquery_table`
partition by timestamp_trunc(created_at, day)
as (
select
user_id,
event_name,
created_at
from `analytics`.`events`
)
"수집(ingestion)" 날짜 또는 타임스탬프로 파티셔닝
BigQuery는 각 행이 수집된 시간을 기준으로 하는 더 오래된 파티셔닝 메커니즘을 지원해요. 가능하면 더 새롭고 사용하기 편리한 파티셔닝 방식을 권장하지만, 매우 큰 데이터셋의 경우 이 오래된 방식이 성능상 이점을 줄 때도 있어요. 아래의 insert_overwrite 점진적 전략에 대해 더 읽어보세요.
dbt는 항상 partition_by.field에 지정된 컬럼의 값으로 테이블을 파티셔닝하도록 BigQuery에 지시합니다. 모델을 partition_by.time_ingestion_partitioning을 True로 설정해 구성하면, dbt는 해당 컬럼을 _PARTITIONTIME 의사컬럼(pseudocolumn)의 입력으로 사용합니다. 새 컬럼 기반 파티셔닝과 달리, 파티셔닝 컬럼의 값이 파티션의 시간 기반 단위와 정확히 일치하도록 직접 보장해야 해요.
bigquery_table.sql
{{ config(
materialized="incremental",
partition_by={
"field": "created_date",
"data_type": "timestamp",
"granularity": "day",
"time_ingestion_partitioning": true
}
) }}
select
user_id,
event_name,
created_at,
-- 이 컬럼의 값은 위에서 정의한 데이터 타입 + 단위와 일치해야 함
timestamp_trunc(created_at, day) as created_date
from {{ ref('events') }}
컴파일된 코드:
create table `projectname`.`analytics`.`bigquery_table` (`user_id` INT64, `event_name` STRING, `created_at` TIMESTAMP)
partition by timestamp_trunc(_PARTITIONTIME, day);
insert into `projectname`.`analytics`.`bigquery_table` (_partitiontime, `user_id`, `event_name`, `created_at`)
select created_date as _partitiontime, * EXCEPT(created_date) from (
select
user_id,
event_name,
created_at,
-- 이 컬럼의 값은 위에서 정의한 단위와 일치해야 함
timestamp_trunc(created_at, day) as created_date
from `projectname`.`analytics`.`events`
);
정수 버킷으로 파티셔닝
data_type이 int64로 지정되면 partition_by 사전에 range 키도 반드시 함께 제공해야 해요. dbt는 range 사전의 값을 사용해 테이블의 파티셔닝 절을 생성합니다.
bigquery_table.sql
{{ config(
materialized='table',
partition_by={
"field": "user_id",
"data_type": "int64",
"range": {
"start": 0,
"end": 100,
"interval": 10
}
}
)}}
select
user_id,
event_name,
created_at
from {{ ref('events') }}
컴파일된 코드:
create table analytics.bigquery_table
partition by range_bucket(
user_id,
generate_array(0, 100, 10)
)
as (
select
user_id,
event_name,
created_at
from analytics.events
)
추가 파티션 구성
모델에 partition_by가 구성되어 있다면, 다음 두 가지 구성을 선택적으로 지정할 수 있어요.
require_partition_filter(boolean):true로 설정하면 이 모델을 조회하는 모든 사용자는 파티션 필터를 반드시 지정해야 하며, 그렇지 않으면 쿼리가 실패해요. 일별로 묶인 이벤트 스트림처럼 파티셔닝 구조가 명확한 매우 큰 테이블에 권장됩니다. 이 설정은 이 모델에서 select하려는 다른 dbt 모델이나 테스트에도 영향을 준다는 점을 유의하세요.partition_expiration_days(integer): date 또는 timestamp 유형 파티션에 설정하면, 파티션은 그것이 나타내는 날짜로부터 그 일수만큼 지난 후에 만료됩니다. 예를 들어2021-01-01을 나타내는 파티션이 7일 뒤 만료되도록 설정되면2021-01-08부터 더 이상 조회할 수 없고, 저장 비용은 0이 되며 내용물은 결국 삭제돼요. 테이블 만료가 지정되면 우선 적용된다는 점을 유의하세요.
bigquery_table.sql
{{ config(
materialized = 'table',
partition_by = {
"field": "created_at",
"data_type": "timestamp",
"granularity": "day"
},
require_partition_filter = true,
partition_expiration_days = 7
)}}
클러스터링 절(Clustering clause)
BigQuery 테이블은 관련 데이터를 함께 배치하기 위해 클러스터링될 수 있어요.
단일 컬럼으로 클러스터링:
bigquery_table.sql
{{
config(
materialized = "table",
cluster_by = "order_id",
)
}}
select * from ...
여러 컬럼으로 클러스터링:
bigquery_table.sql
{{
config(
materialized = "table",
cluster_by = ["customer_id", "order_id"],
)
}}
select * from ...
예약(Reservations) 사용하기
reservation 구성은 dbt가 제출한 BigQuery 작업을 특정 예약으로 보냅니다.
reservation은 우선순위가 낮은 것부터 높은 것 순으로 세 가지 수준에서 설정할 수 있어요.
- 대상(Target) 수준 (
profiles.yml) — 해당 대상의 모든 작업에 적용됩니다. Connect BigQuery를 참고하세요. - 프로젝트 수준 (
dbt_project.yml) — 일치하는 모든 모델에 적용됩니다.
dbt_project.yml
models:
my_project:
+reservation: 'projects/abc-123/locations/US/reservations/my-reservation'
- 모델 수준 (
{{ config(...) }}) — 단일 모델에 대해 프로젝트 및 대상 설정을 덮어씁니다.
models/my_model.sql
{{ config(
reservation='projects/abc-123/locations/US/reservations/my-reservation'
) }}
select ...
KMS 암호화 관리
고객 관리 암호화 키는 kms_key_name 모델 구성으로 BigQuery 테이블에 대해 구성할 수 있어요.
KMS 암호화 사용하기
모델(또는 모델 그룹)의 KMS 키 이름을 지정하려면 kms_key_name 모델 구성을 사용하세요. 다음 예시는 dbt 프로젝트의 encrypted/ 디렉터리에 있는 모든 모델에 kms_key_name을 설정합니다.
dbt_project.yml
name: my_project
version: 1.0.0
...
models:
my_project:
encrypted:
+kms_key_name: 'projects/PROJECT_ID/locations/global/keyRings/test/cryptoKeys/quickstart'
라벨과 태그
라벨 지정하기
dbt는 생성하는 테이블과 뷰에 대해 BigQuery 라벨 지정을 지원해요. 이 라벨은 labels 모델 구성으로 지정할 수 있습니다.
labels 구성은 모델 구성이나 아래와 같이 dbt_project.yml 파일에 제공할 수 있어요.
63자보다 긴 라벨의 BigQuery 키-값 쌍 항목은 잘립니다.
모델 파일에서 라벨 구성하기
model.sql
{{
config(
materialized = "table",
labels = {'contains_pii': 'yes', 'contains_pie': 'no'}
)
}}
select * from {{ ref('another_model') }}
dbt_project.yml에서 라벨 구성하기
dbt_project.yml
models:
my_project:
snowplow:
+labels:
domain: clickstream
finance:
+labels:
domain: finance
BigQuery 콘솔에서 라벨 보기
작업에 라벨 적용하기
labels 구성이 dbt가 만든 테이블과 뷰에 라벨을 적용하는 반면, dbt가 실행하는 BigQuery *작업(jobs)*에도 라벨을 적용할 수 있어요. 작업 라벨은 쿼리 비용 추적, 작업 성능 모니터링, dbt 메타데이터로 BigQuery 작업 이력 구성에 유용합니다.
기본적으로 라벨은 작업에 직접 적용되지 않아요. 하지만 쿼리 주석을 통해 작업 라벨링을 다음과 같은 단계로 활성화할 수 있습니다.
1단계
쿼리 주석을 통해 쿼리에 라벨을 추가하는 query_comment 매크로를 정의하세요.
-- macros/query_comment.sql
{% macro query_comment(node) %}
{%- set comment_dict = {} -%}
{%- do comment_dict.update(
app='dbt',
dbt_version=dbt_version,
profile_name=target.get('profile_name'),
target_name=target.get('target_name'),
) -%}
{%- if node is not none -%}
{%- do comment_dict.update(node.config.get("labels", {})) -%}
{% else %}
{%- do comment_dict.update(node_id='internal') -%}
{%- endif -%}
{% do return(tojson(comment_dict)) %}
{% endmacro %}
이 매크로는 dbt 메타데이터(app, version, profile, target)를 담은 JSON 주석을 만들고, 구성한 모델별 라벨을 병합합니다.
2단계
dbt_project.yml에서 query-comment 구성에 comment: "{{ query_comment(node) }}"와 job-label: true를 설정해 작업 라벨링을 활성화하세요.
# dbt_project.yml
name: analytics
profile: bq
version: "1.0.0"
models:
analytics:
+materialized: table
query-comment:
comment: "{{ query_comment(node) }}"
job-label: true
활성화되면 BigQuery가 JSON 주석을 파싱해 키-값 쌍을 각 작업의 라벨로 적용합니다. 이후 BigQuery 콘솔이나 INFORMATION_SCHEMA.JOBS 뷰에서 이 라벨로 작업을 필터링하고 분석할 수 있어요.
태그 지정하기
BigQuery 테이블과 뷰 태그는 라벨 값에 빈 문자열을 제공해 만들 수 있어요.
model.sql
{{
config(
materialized = "table",
labels = {'contains_pii': ''}
)
}}
select * from {{ ref('another_model') }}
값이 없는 새 라벨을 만들거나 기존 라벨 키에서 값을 제거할 수 있습니다.
값이 비어 있는 키의 라벨은 BigQuery에서 태그라고도 부를 수 있어요. 하지만 이는 BigQuery 테이블과 데이터셋에 IAM 정책을 조건부로 적용하는 BigQuery 태그와는 다릅니다. 자세한 내용은 Tags 문서를 참고하세요.
리소스 태그
BigQuery 태그는 BigQuery 테이블과 뷰에 대한 조건부 IAM 접근 제어를 가능하게 해요. 이 BigQuery 태그는 resource_tags 구성으로 적용할 수 있습니다. 이 절에서는 resource_tags 구성 파라미터 사용 지침을 안내합니다.
리소스 태그는 BigQuery의 태그 형식 {google_cloud_project_id}/{key_name}: value를 따라야 하는 키-값 쌍이에요. 라벨과 달리 BigQuery 태그는 주로 조건부 정책을 사용한 IAM 접근 제어를 위해 설계되어, 조직에서는 다음을 할 수 있습니다.
- 조건부 접근 제어 구현: BigQuery 태그를 기반으로 IAM 정책을 조건부로 적용(예:
environment:production으로 태그된 테이블에만 접근 허용). - 데이터 거버넌스 강화: IAM 정책과 함께 BigQuery 태그를 사용해 민감한 데이터 보호.
- 규모에 맞는 접근 제어: 여러 프로젝트와 환경에서 일관된 접근 패턴 관리.
사전 요구사항
- dbt에서 사용하기 전에 태그 키와 값을 미리 생성하세요.
- 리소스에 태그를 적용할 필요한 IAM 권한을 부여하세요.
모델 파일에서 태그 구성하기
모델 파일에서 태그를 구성하려면 다음 예시를 참고하세요.
model.sql
{{
config(
materialized = "table",
resource_tags = {
"my-project-id/environment": "production",
"my-project-id/data_classification": "sensitive",
"my-project-id/access_level": "restricted"
}
)
}}
select * from {{ ref('another_model') }}
dbt_project.yml에서 태그 구성하기
dbt_project.yml 파일에서 태그를 구성하려면 다음 예시를 참고하세요.
dbt_project.yml
models:
my_project:
production:
+resource_tags:
my-project-id/environment: production
my-project-id/data_classification: sensitive
staging:
+resource_tags:
my-project-id/environment: staging
my-project-id/data_classification: internal
dbt 태그와 BigQuery 태그 모두 사용하기
dbt의 기존 tags 구성과 BigQuery의 resource_tags를 함께 사용할 수 있어요.
model.sql
{{
config(
materialized = "materialized_view",
tags = ["reporting", "daily"], # 내부 조직을 위한 dbt 태그
resource_tags = { # IAM 접근 제어를 위한 BigQuery 태그
"my-project-id/environment": "production",
"my-project-id/data_classification": "sensitive"
}
)
}}
select * from {{ ref('my_table') }}
BigQuery 태그로 IAM 조건부 정책을 설정하는 방법에 대한 자세한 내용은 BigQuery의 태그 문서를 참고하세요.
정책 태그(Policy tags)
BigQuery는 특정 컬럼에 정책 태그를 설정해 컬럼 수준 보안을 지원합니다.
dbt는 이 기능을 컬럼 리소스 속성인 policy_tags로 지원합니다(노드 구성이 아님).
models/
models:
- name: policy_tag_table
columns:
- name: field
policy_tags:
- 'projects/<gcp-project>/locations/<location>/taxonomies/<taxonomy>/policyTags/<tag>'
정책 태그가 적용되려면 모델, 시드 또는 스냅샷에 대해 컬럼 수준 persist_docs가 활성화되어 있어야 합니다. 변수를 사용해 taxonomy를 관리하고, BigQuery 서비스 계정 키에 필요한 보안 역할을 추가하세요.
병합 동작(점진적 모델)
incremental_strategy 구성은 dbt가 점진적(incremental) 모델을 만드는 방식을 제어합니다. dbt는 BigQuery에서 merge 문을 사용해 점진적 테이블을 새로 고칩니다.
incremental_strategy 구성은 다음 값 중 하나로 설정할 수 있어요.
merge(기본값)insert_overwrite- microbatch
변경 이력
enable_change_history 파라미터는 BigQuery 테이블에 대한 변경을 추적하는 BigQuery의 change history 기능을 활성화합니다. 활성화하면 변경 이력을 사용해 점진적 모델의 동작을 감사하고 디버깅할 수 있어요.
enable_change_history는 <boolean> 값으로 설정됩니다.
성능과 비용
dbt가 BigQuery 점진적 모델을 만드는 동안 수행하는 작업은 모델 구성에 클러스터링 절을 사용해 더 저렴하고 빠르게 만들 수 있어요. BigQuery 점진적 모델의 성능 튜닝에 대한 자세한 내용은 이 가이드를 참고하세요.
참고: 이러한 성능 및 비용 이점은 merge 또는 insert_overwrite 점진적 전략으로 만든 점진적 모델에 적용됩니다.
merge 전략
merge 점진적 전략은 다음과 같은 merge 문을 생성합니다.
merge into {{ destination_table }} DEST
using ({{ model_sql }}) SRC
on SRC.{{ unique_key }} = DEST.{{ unique_key }}
when matched then update ...
when not matched then insert ...
merge 방식은 대상 점진적 테이블의 새 데이터를 자동으로 업데이트하지만, 모델 SQL에서 참조되는 모든 원본 테이블과 대상 테이블을 스캔해야 해요. 대량의 데이터에서는 느리고 비쌀 수 있습니다. 앞서 언급한 파티셔닝과 클러스터링 기법이 이런 문제를 완화하는 데 도움이 돼요.
참고: merge 점진적 전략을 선택하면 unique_key 구성이 필요합니다.
insert_overwrite 전략
insert_overwrite 전략은 대상 테이블의 파티션 전체를 교체하는 merge 문을 생성합니다. 참고: 이 구성은 모델이 파티션 절로 구성되어 있어야 해요. insert_overwrite 전략을 선택하면 dbt가 생성하는 merge 문은 대략 다음과 같습니다.
/*
모델 SQL로 임시 테이블 생성
*/
create temporary table {{ model_name }}__dbt_tmp as (
{{ model_sql }}
);
/*
해당되는 경우, 임시 테이블을 조회해
교체할 파티션을 결정.
*/
declare dbt_partitions_for_replacement array<date>;
set (dbt_partitions_for_replacement) = (
select as struct
array_agg(distinct date(max_tstamp))
from `my_project`.`my_dataset`.{{ model_name }}__dbt_tmp
);
/*
임시 테이블의 파티션과 일치하는
대상 테이블의 파티션을 교체
*/
merge into {{ destination_table }} DEST
using {{ model_name }}__dbt_tmp SRC
on FALSE
when not matched by source and {{ partition_column }} in unnest(dbt_partitions_for_replacement)
then delete
when not matched then insert ...
이 방식의 메커니즘에 대한 전체 설명은 이 설명 게시물을 참고하세요.
교체할 파티션 결정
dbt는 임시 테이블에 존재하는 값에서 동적으로, 또는 사용자가 제공한 구성으로 정적으로 교체할 파티션을 결정할 수 있어요.
"동적(dynamic)" 방식이 가장 단순하고(기본값) "정적(static)" 방식은 모델 빌드 스크립트의 여러 쿼리를 제거해 비용을 줄입니다.
정적 파티션
교체할 파티션의 정적 목록을 제공하려면 partitions 구성을 사용하세요.
models/session.sql
{% set partitions_to_replace = [
'timestamp(current_date)',
'timestamp(date_sub(current_date, interval 1 day))'
] %}
{{
config(
materialized = 'incremental',
incremental_strategy = 'insert_overwrite',
partition_by = {'field': 'session_start', 'data_type': 'timestamp'},
partitions = partitions_to_replace
)
}}
with events as (
select * from {{ref('events')}}
{% if is_incremental() %}
-- 어제 + 오늘 재계산
where timestamp_trunc(event_timestamp, day) in ({{ partitions_to_replace | join(',') }})
{% endif %}
),
... rest of model ...
이 예시 모델은 실행할 때마다 대상 테이블의 데이터를 오늘과 어제 모두에 대해 교체합니다. dbt로 테이블을 점진적으로 업데이트하는 가장 빠르고 저렴한 방법이에요. 더 동적으로 실행하고 싶다면 — 예를 들어 항상 지난 3일을 — dbt에 내장된 datetime 매크로를 활용하고 몇 개를 직접 작성할 수 있어요.
이것을 "완전 제어(full control)" 모드라고 생각하면 돼요. partitions 구성의 표현식이나 리터럴 값은 템플릿화될 때 적절한 따옴표가 붙어야 하고, partition_by.data_type(timestamp , datetime , date 또는 int64 )과 일치해야 합니다. 그렇지 않으면 점진적 merge 문의 필터가 오류를 일으켜요.
동적 파티션
partitions 구성이 제공되지 않으면 dbt는 대신 다음을 수행합니다.
- 모델 SQL용 임시 테이블 생성
- 임시 테이블을 조회해 교체할 고유 파티션 찾기
- 대상 테이블을 조회해 데이터베이스의 최대 파티션 찾기
모델 SQL을 작성할 때 dbt가 수행하는 인트로스펙션을 활용해 새로운 데이터만 필터링할 수 있어요. 대상 테이블의 파티션 필드 최대값은 _dbt_max_partition BigQuery 스크립팅 변수로 사용할 수 있습니다. 참고: 이것은 BigQuery SQL 변수이지 dbt Jinja 변수가 아니므로, 이 변수에 접근할 때는 Jinja 괄호가 필요하지 않아요.
예시 모델 SQL:
{{
config(
materialized = 'incremental',
partition_by = {'field': 'session_start', 'data_type': 'timestamp'},
incremental_strategy = 'insert_overwrite'
)
}}
with events as (
select * from {{ref('events')}}
{% if is_incremental() %}
-- 최신 날짜 데이터 + 이전 재계산
-- 참고: _dbt_max_partition 변수는 대상 테이블을 인트로스펙션하는 데 사용됨
where date(event_timestamp) >= date_sub(date(_dbt_max_partition), interval 1 day)
{% endif %}
),
... rest of model ...
파티션 복사하기
점진적 실행에서 전체 파티션을 교체하는 경우, merge 문 대신 copy table API와 파티션 데코레이터로 처리할 수 있어요. 이 메커니즘은 SQL merge 문만큼의 가시성이나 디버깅 용이성은 없지만, copy table API는 데이터 삽입 비용이 들지 않아 대용량 데이터셋에서 시간과 비용을 크게 절약할 수 있어요. 이는 bq cp gcloud 명령줄 인터페이스(CLI) 명령과 동일합니다.
partition_by 구성에서 copy_partitions: True를 켜서 활성화할 수 있어요. 이 방식은 "동적" 파티션 교체와 함께만 작동합니다.
bigquery_table.sql
{{ config(
materialized="incremental",
incremental_strategy="insert_overwrite",
partition_by={
"field": "created_date",
"data_type": "timestamp",
"granularity": "day",
"time_ingestion_partitioning": true,
"copy_partitions": true
}
) }}
select
user_id,
event_name,
created_at,
-- 이 컬럼의 값은 위에서 정의한 데이터 타입 + 단위와 일치해야 함
timestamp_trunc(created_at, day) as created_date
from {{ ref('events') }}
logs/dbt.log
...
[0m16:03:13.017641 [debug] [Thread-3 (]: BigQuery adapter: Copying table(s) "/projects/projectname/datasets/analytics/tables/bigquery_table__dbt_tmp$20230112" to "/projects/projectname/datasets/analytics/tables/bigquery_table$20230112" with disposition: "WRITE_TRUNCATE"
...
테이블 만료 제어
기본적으로 dbt가 만든 테이블은 만료되지 않아요. hours_to_expiration을 설정하면 특정 모델이 정해진 시간 후에 만료되도록 구성할 수 있습니다.
dbt_project.yml
models:
<resource-path>:
+hours_to_expiration: 6
models/
{{ config(
hours_to_expiration = 6
) }}
select ...
hours_to_expiration 구성은 기본 테이블이 처음 생성될 때만 적용됩니다. 점진적 실행에서는 재설정되지 않아요. 이 문제를 해결하려면 +post-hook에서 만료 타임스탬프를 수동으로 재설정하는 매크로를 호출하세요. hours 인자를 hours_to_expiration 값과 일치하도록 수정해야 합니다.
예시 매크로 SQL:
{% macro reset_expiration_for_incremental(hours) %}
-- 현재 모델이 점진적으로 실행 중인지 확인
{% if is_incremental() %}
-- 생성 시에만 적용되고 merge 때는 적용되지 않으므로 만료 타임스탬프 설정
ALTER TABLE {{ this }}
SET OPTIONS (expiration_timestamp = TIMESTAMP_ADD(CURRENT_TIMESTAMP(), INTERVAL {{ hours }} HOUR))
{% endif %}
{% endmacro %}
dbt_project.yml
models:
my_project:
+post-hook:
- "{{ reset_expiration_for_incremental(hours=6) }}"
승인된 뷰(Authorized views)
뷰로 구체화된 모델에 grant_access_to 구성이 지정되면, dbt는 뷰 모델에 제공된 데이터셋 목록에서 select할 수 있는 접근 권한을 부여합니다. 자세한 내용은 BQ의 승인된 뷰 문서를 참고하세요.
참고: grants 구성과 grant_access_to 구성은 서로 다릅니다.
grant_access_to: 승인된 뷰를 설정할 수 있게 해줘요. 구성되면 dbt는 다른 데이터셋의 일부 정보를 보여줄 수 있는 승인된 뷰 접근을 제공하되, 최종 사용자에게 그 기본 데이터셋 전체 접근은 부여하지 않습니다. 자세한 내용은 "BigQuery 구성: 승인된 뷰"를 참고하세요.grants: dbt로 만들고 있는 데이터셋에 대한 접근을 관리하기 위해 사용자, 그룹 또는 서비스 계정에 특정 권한을 제공합니다. 자세한 내용은 "리소스 구성: grants"를 참고하세요.- 두 기능을 함께 사용할 수 있어요:
grants_access_to구성으로 뷰 모델을 "승인(authorize)"하고 그 뷰 모델에grants를 추가해 조회 결과(그리고 오직 조회 결과만)를 다른 사용자, 그룹 또는 서비스 계정과 공유할 수 있습니다.
dbt_project.yml
models:
<resource-path>:
+grant_access_to:
- project: project_1
dataset: dataset_1
- project: project_2
dataset: dataset_2
models/
{{ config(
grant_access_to=[
{'project': 'project_1', 'dataset': 'dataset_1'},
{'project': 'project_2', 'dataset': 'dataset_2'}
]
) }}
이 구성이 있는 뷰는 객체가 다른 곳에 위치하고 project_1.dataset_1과 project_2.dataset_2에 접근 권한이 없는 사용자가 조회하더라도, 해당 데이터셋의 객체에서 select할 수 있습니다.
구체화된 뷰(Materialized views)
BigQuery 어댑터는 다음 구성 파라미터로 구체화된 뷰를 지원합니다.
| 파라미터 | 유형 | 필수 | 기본값 | 변경 모니터링 |
|---|---|---|---|---|
| on_configuration_change | no | apply | n/a | |
| cluster_by | [ |
no | none | drop/create |
| partition_by | { |
no | none | drop/create |
| enable_refresh | no | true | alter | |
| refresh_interval_minutes | no | 30 | alter | |
| max_staleness (in Preview) | no | none | alter | |
| description | no | none | alter | |
| labels | { |
no | none | alter |
| resource_tags | { |
no | none | alter |
| hours_to_expiration | no | none | alter | |
| kms_key_name | no | none | alter |
- 프로젝트 파일 / 속성 파일 / SQL 파일 구성
dbt_project.yml
models:
<resource-path>:
+materialized: materialized_view
+on_configuration_change: apply | continue | fail
+cluster_by: <field-name> | [<field-name>]
+partition_by:
- field: <field-name>
- data_type: timestamp | date | datetime | int64
# `data_type`이 'int64'가 아닌 경우에만
- granularity: hour | day | month | year
# `data_type`이 'int64'인 경우에만
- range:
- start: <integer>
- end: <integer>
- interval: <integer>
+enable_refresh: true | false
+refresh_interval_minutes: <float>
+max_staleness: <interval>
+description: <string>
+labels: {<label-name>: <label-value>}
+resource_tags: {<tag-key>: <tag-value>}
+hours_to_expiration: <integer>
+kms_key_name: <path-to-key>
models/properties.yml
models:
- name: [<model-name>]
config:
materialized: materialized_view
on_configuration_change: apply | continue | fail
cluster_by: <field-name> | [<field-name>]
partition_by:
- field: <field-name>
- data_type: timestamp | date | datetime | int64
# `data_type`이 'int64'가 아닌 경우에만
- granularity: hour | day | month | year
# `data_type`이 'int64'인 경우에만
- range:
- start: <integer>
- end: <integer>
- interval: <integer>
enable_refresh: true | false
refresh_interval_minutes: <float>
max_staleness: <interval>
description: <string>
labels: {<label-name>: <label-value>}
resource_tags: {<tag-key>: <tag-value>}
hours_to_expiration: <integer>
kms_key_name: <path-to-key>
models/<model_name>.sql
{{ config(
materialized='materialized_view',
on_configuration_change="apply" | "continue" | "fail",
cluster_by="<field-name>" | ["<field-name>"],
partition_by={
"field": "<field-name>",
"data_type": "timestamp" | "date" | "datetime" | "int64",
# `data_type`이 'int64'가 아닌 경우에만
"granularity": "hour" | "day" | "month" | "year",
# `data_type`이 'int64'인 경우에만
"range": {
"start": <integer>,
"end": <integer>,
"interval": <integer>,
}
},
# 자동 새로고침 옵션
enable_refresh= true | false,
refresh_interval_minutes=<float>,
max_staleness="<interval>",
# 추가 옵션
description="<description>",
labels={
"<label-name>": "<label-value>",
},
resource_tags={
"<tag-key>": "<tag-value>",
},
hours_to_expiration=<integer>,
kms_key_name="<path_to_key>",
) }}
이 중 많은 파라미터가 테이블의 대응 파라미터와 일치하며 위에 링크되어 있어요. 구체화된 뷰에 고유한 파라미터 집합은 자동 새로고침 기능을 다룹니다.
이 파라미터에 대한 자세한 내용은 BigQuery 문서에서 확인하세요.
자동 새로고침
| 파라미터 | 유형 | 필수 | 기본값 | 변경 모니터링 |
|---|---|---|---|---|
| enable_refresh | no | true | alter | |
| refresh_interval_minutes | no | 30 | alter | |
| max_staleness (in Preview) | no | none | alter |
BigQuery는 구체화된 뷰에 대한 자동 새로고침 구성을 지원해요. 기본적으로 구체화된 뷰는 기본 테이블의 변경 후 5분 이내에 자동으로 새로고침되지만, 30분에 한 번보다 자주는 아닙니다. BigQuery는 공식적으로 빈도(즉 "30분에 한 번" 빈도)의 구성만 지원하지만, staleness("5분" 새로고침) 구성을 허용하는 미리보기(preview) 기능이 있어요. dbt는 이 파라미터의 변경을 모니터링하고 ALTER 문으로 적용합니다.
이 파라미터에 대한 자세한 내용은 BigQuery 문서에서 확인하세요.
제한 사항
대부분의 데이터 플랫폼과 마찬가지로 구체화된 뷰에도 제한 사항이 있어요. 주목할 만한 몇 가지는 다음과 같습니다.
- 구체화된 뷰 SQL은 제한된 기능 집합을 가집니다.
- 구체화된 뷰 SQL은 업데이트할 수 없습니다. 구체화된 뷰는
--full-refresh(DROP/CREATE)를 거쳐야 해요. - 구체화된 뷰의
partition_by절은 기본(base) 테이블의 것과 일치해야 합니다. - 구체화된 뷰는 description을 가질 수 있지만 구체화된 뷰 컬럼은 가질 수 없어요.
- 기본 테이블을 다시 만들거나 드롭하려면 구체화된 뷰도 다시 만들거나 드롭해야 합니다.
Google BigQuery 문서에서 구체화된 뷰 제한 사항에 대한 더 많은 정보를 확인하세요.
Python 모델 구성
제출 방법(Submission methods):
BigQuery는 Python 코드를 제출하는 몇 가지 서로 다른 메커니즘을 지원하며, 각각 상대적인 장점이 있어요. dbt-bigquery 어댑터는 BigQuery DataFrames(BigFrames) 또는 Dataproc을 사용합니다. 이 과정은 BigQuery에서 데이터를 읽고, BigQuery DataFrames 또는 Dataproc으로 네이티브하게 계산한 뒤 결과를 다시 BigQuery에 씁니다.
- BigQuery DataFrames / Dataproc
BigQuery DataFrames는 pandas와 scikit-learn을 실행할 수 있어요. 인프라를 관리할 필요가 없고 BigQuery 분산 쿼리 엔진을 활용합니다. pandas와 유사한 구문으로 빅데이터를 다루고 싶은 분석가, 데이터 과학자, 머신러닝 엔지니어에게 훌륭합니다. 참고: BigQuery DataFrames는 Google Colab의 기본 런타임에서 실행됩니다. default 런타임 템플릿이 없으면 어댑터가 자동으로 하나를 만들어 다음 사용을 위해 default로 표시합니다(올바른 권한이 있다고 가정). BigQuery DataFrames 설정:
# IAM permission if using service account
#Create Service Account
gcloud iam service-accounts create dbt-bigframes-sa
#Grant BigQuery User Role
gcloud projects add-iam-policy-binding ${GOOGLE_CLOUD_PROJECT} --member=serviceAccount:dbt-bigframes-sa@${GOOGLE_CLOUD_PROJECT}.iam.gserviceaccount.com --role=roles/bigquery.user
#Grant BigQuery Data Editor role. This can be restricted at dataset level
gcloud projects add-iam-policy-binding ${GOOGLE_CLOUD_PROJECT} --member=serviceAccount:dbt-bigframes-sa@${GOOGLE_CLOUD_PROJECT}.iam.gserviceaccount.com --role=roles/bigquery.dataEditor
#Grant Service Account user
gcloud projects add-iam-policy-binding ${GOOGLE_CLOUD_PROJECT} --member=serviceAccount:dbt-bigframes-sa@${GOOGLE_CLOUD_PROJECT}.iam.gserviceaccount.com --role=roles/iam.serviceAccountUser
#Grant Colab Enterprise User
gcloud projects add-iam-policy-binding ${GOOGLE_CLOUD_PROJECT} --member=serviceAccount:dbt-bigframes-sa@${GOOGLE_CLOUD_PROJECT}.iam.gserviceaccount.com --role=roles/aiplatform.colabEnterpriseUser
dbt_project.yml:
models:
my_dbt_project:
submission_method: bigframes
profiles.yml:
my_dbt_project_sa:
outputs:
dev:
compute_region: us-central1
dataset: <BIGQUERY_DATASET>
gcs_bucket: <GCS BUCKET USED FOR BIGFRAME LOGS>
job_execution_timeout_seconds: 300
job_retries: 1
keyfile: <SERVICE ACCOUNT KEY FILE>
location: US
method: service-account
priority: interactive
project: <BIGQUERY PROJECT ID>
type: bigquery
Dataproc(serverless 또는 사전 구성된 cluster)은 Python 모델을 PySpark 작업으로 실행해 BigQuery에서 읽고 쓸 수 있어요. serverless는 더 단순하지만 구성과 사전 설치된 패키지(pandas , numpy , scikit-learn)가 제한적이며 더 느립니다. 반면 cluster는 완전한 제어와 더 빠른 실행 시간을 제공합니다. 복잡하고 오래 실행되는 배치 파이프라인과 레거시 Hadoop/Spark 워크플로에 좋지만, 임시(ad-hoc) 또는 대화형 작업에는 종종 더 느립니다. Dataproc 설정:
- Cloud Storage 버킷을 만들거나 기존 것을 사용하세요.
- 프로젝트와 지역에 Dataproc API를 활성화하세요.
cluster제출 방법을 사용하는 경우: Spark BigQuery 커넥터 초기화 액션을 사용해 Dataproc 클러스터를 만들거나 기존 것을 사용하세요. (Google은 스크린샷에 보이는 예시 버전 대신 액션을 자신의 Cloud Storage 버킷에 복사하는 것을 권장합니다.)
Dataproc에서 Python 모델을 실행하려면 다음 구성이 필요합니다. BigQuery 프로필에 추가하거나 특정 Python 모델에 구성할 수 있어요.
gcs_bucket: dbt가 모델의 컴파일된 PySpark 코드를 업로드할 Storage 버킷.dataproc_region: Dataproc을 활성화한 GCP 지역(예:us-central1).dataproc_cluster_name: Python 모델 실행(PySpark 작업 실행)에 사용할 Dataproc 클러스터 이름.submission_method: cluster일 때만 필요.
def model(dbt, session):
dbt.config(
submission_method="cluster",
dataproc_cluster_name="my-favorite-cluster"
)
...
models:
- name: my_python_model
config:
submission_method: serverless
Dataproc Serverless에서 실행되는 Python 모델은 BigQuery 프로필에서 추가로 구성할 수 있어요. dbt Python 모델을 실행하는 모든 사용자 또는 서비스 계정은 필수 BigQuery 권한 외에 다음 권한이 필요합니다.
dataproc.batches.create
dataproc.clusters.use
dataproc.jobs.create
dataproc.jobs.get
dataproc.operations.get
dataproc.operations.list
storage.buckets.get
storage.objects.create
storage.objects.delete
자세한 내용은 Dataproc IAM 역할과 권한을 참고하세요.
패키지 설치: Dataproc에서 타사 패키지 설치는 cluster인지 serverless인지에 따라 다릅니다.
Dataproc Cluster — Google은 초기화 액션을 통해 클러스터 생성 시 Python 패키지를 설치할 것을 권장합니다.
클러스터 생성 시 클러스터 속성 정의로 패키지를 설치할 수도 있어요: dataproc:pip.packages 또는 dataproc:conda.packages.
Dataproc Serverless — Google은 타사 패키지 설치에 커스텀 docker 이미지를 사용할 것을 권장합니다. 이미지는 Google Artifact Registry에 호스팅되어야 해요. 그런 다음 dbt 프로필에서 이미지 경로를 제공해 사용할 수 있습니다.
profiles.yml:
my-profile:
target: dev
outputs:
dev:
type: bigquery
method: oauth
project: abc-123
dataset: my_dataset
# Dataproc Serverless에서 실행될 dbt Python 모델용
gcs_bucket: dbt-python
dataproc_region: us-central1
submission_method: serverless
dataproc_batch:
runtime_config:
container_image: {HOSTNAME}/{PROJECT_ID}/{IMAGE}:{TAG}
클러스터 시작 시 pip로 설치할 패키지 추가
추가 파라미터
BigQuery Python 모델에는 다음과 같은 추가 구성 파라미터도 있어요.
| 파라미터 | 유형 | 필수 | 기본값 | 유효 값 |
|---|---|---|---|---|
| enable_list_inference | no | True | True , False | |
| intermediate_format | no | parquet | parquet , orc | |
| submission_method | no | `` | serverless , bigframes , cluster | |
| notebook_template_id | no | `` | ||
| compute_region | no | `` | <COMPUTE_REGION> | |
| gcs_bucket | no | `` | <GCS_BUCKET> | |
| packages | no | `` | ['numpy<=1.1.1', 'pandas', 'mlflow'] | |
| timeout | no | `` | <timeout_in_seconds> |
enable_list_inference파라미터enable_list_inference파라미터는 PySpark 데이터 프레임이 동일한 작업에서 여러 레코드를 읽을 수 있게 합니다. 기본intermediate_format인parquet을 지원하기 위해 기본값은True로 설정됩니다.intermediate_format파라미터intermediate_format파라미터는 레코드를 테이블에 쓸 때 사용할 파일 형식을 지정합니다. 기본값은parquet이에요.submission_method파라미터submission_method파라미터는 작업이 BigQuery DataFrames에서 실행될지 Serverless Spark에서 실행될지 지정합니다.dataproc_cluster_name이 선언되면submission_method는 필요하지 않아요.notebook_template_id파라미터notebook_template_id파라미터는 Colab Enterprise의 런타임 템플릿을 지정합니다.compute_region파라미터compute_region파라미터는 작업의 지역을 지정합니다.gcs_bucket파라미터gcs_bucket파라미터는 작업의 아티팩트 저장에 사용되는 GCS 버킷을 지정합니다.timeout파라미터timeout파라미터는 Python 모델의 최대 실행 시간(초)을 지정합니다. 복잡한 데이터 처리나 머신러닝 워크로드에 더 긴 실행 시간이 필요한 BigFrames 모델에 특히 유용해요. 지정하지 않으면 실행 환경에 구성된 기본 타임아웃을 사용합니다.
관련 문서:
단위 테스트 제한 사항
단위 테스트를 위해서는 BigQuery STRUCT의 모든 필드를 지정해야 해요. STRUCT에서 필드의 부분집합만 사용할 수는 없습니다.
더 알아보기 (Learn more)
BigQuery 구성은 dbt의 클라우드 어댑터별 리소스 구성 중 하나예요. 점진적 전략 일반 개념과 다른 어댑터의 구성(예: Snowflake, Databricks, Redshift)도 함께 학습하면 dbt의 어댑터 추상화를 더 잘 이해할 수 있습니다.