Cấu trúc project và materialization
Bạn sẽ học được
- Vai trò của
dbt_project.yml,profiles.ymlvà 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,tablevàephemeraldự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 | Ở đâu | Trả lời câu hỏi |
|---|---|---|
dbt_project.yml | Trong repo, commit vào Git | Project tên gì, model nằm đâu, materialization mặc định |
profiles.yml | Ngoà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:
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: 8Chạ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ại | dbt làm gì | Dùng khi |
|---|---|---|
view | CREATE VIEW | Staging, dữ liệu nhỏ, cần luôn mới |
table | CREATE TABLE AS (drop và tạo lại) | Mart, bảng được đọc nhiều lần |
incremental | Chỉ thêm/merge phần mới | Bảng lớn, chỉ có dữ liệu tăng dần |
ephemeral | Không tạo object, nhúng thành CTE | Bướ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:
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: martsDấ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:
{{ 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:
-- 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 |
|---|---|
+tags | Gắn nhãn để chọn --select tag:nightly |
+persist_docs | Đẩy description thành comment trong kho |
+enabled | Tắt model mà không xoá file |
+docs | Trỏ tới node tài liệu dùng chung |
vars | Biến toàn project, đọc bằng var() |
Thực hành
- Tạo
profiles.ymlvới hai targetdevvàprod, dùngenv_varcho mật khẩu. - Chạy
dbt debugđể xác nhận kết nối. - Khai báo
+materializedtheo thư mục trongdbt_project.yml. - Viết macro
generate_schema_namevà xác nhận schema đúng ở cả hai target. - Chạy
dbt run --select marts --target prod, sau đó kiểm tra schema bằng\dntrong psql.
Lỗi thường gặp
| Triệu chứng | Nguyên nhân | Cách sửa |
|---|---|---|
| Dev ghi đè dữ liệu prod | Dùng chung schema cho hai target | Tách schema, dùng generate_schema_name |
dbt debug báo sai mật khẩu | Mật khẩu ghi cứng hoặc env_var chưa đặt | Dùng env_var, export biến trước khi chạy |
| Đổi materialization không có hiệu lực | config() trong file ghi đè dbt_project.yml | Kiểm tra thứ tự ưu tiên: file thắng |
Bảng table cũ vẫn còn sau khi đổi sang view | dbt không tự xoá object khác loại | Chạy --full-refresh, hoặc xoá tay một lần |
| Dashboard đọc bảng rỗng ban đêm | Mart table đang được drop và tạo lại | Cân nhắc incremental, hoặc chấp nhận cửa sổ build |
Tóm tắt
dbt_project.ymlcommit vào Git;profiles.ymlgiữ ngoài repo.- Target tách môi trường dev và prod,
reftự đổi tên schema. viewcho staging,tablecho mart,incrementalcho bảng lớn,ephemeralcho bước phụ.- Cấu hình theo thư mục bằng
+materializedvà+schema. - Macro
generate_schema_namelà hàng rào bảo vệ production.
Dùng ← → để chuyển bài