DHDataHọc
TheoryLesson 4/85 min read+10 XP

Cấu trúc project và materialization

Bạn sẽ học được

  • Vai trò của dbt_project.yml, profiles.yml và target.
  • Bốn loại materialization và khi nào dùng loại nào.
  • Cách cấu hình materialization theo thư mục thay vì từng file.
  • Tách schema theo môi trường và theo tầng bằng macro generate_schema_name.
  • Chọn giữa view, table và ephemeral dựa trên chi phí thật.

Khái niệm cốt lõi

dbt không tự biết chạy vào đâu. Hai file cấu hình quyết định điều đó:

FileỞ đâuTrả lời câu hỏi
dbt_project.ymlTrong repo, commit vào GitProject tên gì, model nằm đâu, materialization mặc định
profiles.ymlNgoài repo, ~/.dbt/Kết nối kho nào, schema nào, mật khẩu nào

profiles.yml chứa thông tin đăng nhập nên không bao giờ commit. dbt_project.yml chứa quy ước của project nên phải commit.

Một profile có nhiều target — mỗi target là một môi trường:

code
dbt_shop:
  target: dev
  outputs:
    dev:
      type: postgres
      host: localhost
      user: dev
      password: "{{ env_var('DBT_PASSWORD') }}"
      dbname: warehouse
      schema: dev
      threads: 4
    prod:
      type: postgres
      host: warehouse.internal
      user: dbt_prod
      password: "{{ env_var('DBT_PROD_PASSWORD') }}"
      dbname: warehouse
      schema: analytics
      threads: 8

Chạy mặc định vào dev; chạy production bằng --target prod. Nhờ ref, model không cần biết mình đang ở môi trường nào.

Bốn materialization

Loạidbt làm gìDùng khi
viewCREATE VIEWStaging, dữ liệu nhỏ, cần luôn mới
tableCREATE TABLE AS (drop và tạo lại)Mart, bảng được đọc nhiều lần
incrementalChỉ thêm/merge phần mớiBảng lớn, chỉ có dữ liệu tăng dần
ephemeralKhông tạo object, nhúng thành CTEBước trung gian chỉ dùng một lần

ephemeral đáng chú ý: model không tồn tại trong kho. dbt chèn câu SELECT của nó vào model gọi nó dưới dạng CTE. Cách này tránh tạo bảng thừa nhưng làm câu SQL cuối dài và khó đọc — chỉ dùng cho bước phụ nhỏ.

Mặc định trong dbt_project.yml:

code
name: dbt_shop
version: '1.0.0'
profile: dbt_shop

models:
  dbt_shop:
    staging:
      +materialized: view
      +schema: staging
    intermediate:
      +materialized: view
      +schema: intermediate
    marts:
      +materialized: table
      +schema: marts

Dấu + nghĩa là cấu hình áp cho mọi model bên dưới thư mục đó. Một model có thể ghi đè bằng {{ config(...) }} ngay trong file:

code
{{ config(materialized='incremental', unique_key='order_id') }}

select ...

Chi phí thật của view và table

Giả sử dashboard doanh thu được mở 500 lần mỗi ngày.

Nếu mart là view: mỗi lần mở, kho phải join 1.000.000 đơn với 3.000 khách và gộp nhóm. Thời gian đo được 12 giây. Tổng 500 × 12 = 6.000 giây, tức 100 phút CPU mỗi ngày, và người dùng chờ 12 giây mỗi lần.

Nếu mart là table: dbt build một lần mất 40 giây mỗi đêm. Mỗi lần mở dashboard đọc bảng đã gộp, khoảng 0,3 giây. Tổng 40 giây build + 150 giây đọc.

Chênh lệch hơn 100 lần. Đây là lý do mart mặc định là table.

Ngược lại, stg_orders là view: nó chỉ đổi tên và ép kiểu, gần như miễn phí khi đọc. Nếu biến nó thành table, mỗi lần nguồn có dữ liệu mới bạn phải chờ build lại, trong khi lợi ích gần như bằng không.

Tách schema

Mặc định mọi model ghi vào một schema. Muốn tách analytics_staging, analytics_marts, khai báo +schema như trên. dbt ghép tên theo quy tắc <target_schema>_<custom_schema>, cho ra dev_staging, dev_marts.

Nếu muốn tên sạch hơn — staging và marts không có tiền tố môi trường — ghi đè macro:

code
-- macros/generate_schema_name.sql
{% macro generate_schema_name(custom_schema_name, node) -%}
    {%- if custom_schema_name is none -%}
        {{ target.schema }}
    {%- elif target.name == 'prod' -%}
        {{ custom_schema_name | trim }}
    {%- else -%}
        {{ target.schema }}_{{ custom_schema_name | trim }}
    {%- endif -%}
{%- endmacro %}

Kết quả: production ghi vào staging, marts; máy dev ghi vào dev_staging, dev_marts. Hai môi trường không bao giờ ghi đè nhau.

Đây là macro đầu tiên nên viết trong mọi project. Nó là hàng rào giữa máy cá nhân và production.

Cấu hình khác nên biết

Cấu hìnhÝ nghĩa
+tagsGắn nhãn để chọn --select tag:nightly
+persist_docsĐẩy description thành comment trong kho
+enabledTắt model mà không xoá file
+docsTrỏ tới node tài liệu dùng chung
varsBiến toàn project, đọc bằng var()

Thực hành

  1. Tạo profiles.yml với hai target dev và prod, dùng env_var cho mật khẩu.
  2. Chạy dbt debug để xác nhận kết nối.
  3. Khai báo +materialized theo thư mục trong dbt_project.yml.
  4. Viết macro generate_schema_name và xác nhận schema đúng ở cả hai target.
  5. Chạy dbt run --select marts --target prod, sau đó kiểm tra schema bằng \dn trong psql.

Lỗi thường gặp

Triệu chứngNguyên nhânCách sửa
Dev ghi đè dữ liệu prodDùng chung schema cho hai targetTách schema, dùng generate_schema_name
dbt debug báo sai mật khẩuMật khẩu ghi cứng hoặc env_var chưa đặtDùng env_var, export biến trước khi chạy
Đổi materialization không có hiệu lựcconfig() trong file ghi đè dbt_project.ymlKiểm tra thứ tự ưu tiên: file thắng
Bảng table cũ vẫn còn sau khi đổi sang viewdbt không tự xoá object khác loạiChạy --full-refresh, hoặc xoá tay một lần
Dashboard đọc bảng rỗng ban đêmMart table đang được drop và tạo lạiCân nhắc incremental, hoặc chấp nhận cửa sổ build

Tóm tắt

  • dbt_project.yml commit vào Git; profiles.yml giữ ngoài repo.
  • Target tách môi trường dev và prod, ref tự đổi tên schema.
  • view cho staging, table cho mart, incremental cho bảng lớn, ephemeral cho bước phụ.
  • Cấu hình theo thư mục bằng +materialized và +schema.
  • Macro generate_schema_name là hàng rào bảo vệ production.

Use ← → to move between lessons