Tracks
Trong bài viết này, tôi trình bày một số kỹ thuật có thể giúp bạn học dbt và tối ưu hóa việc thiết lập dự án cũng như mô hình hóa dữ liệu, giúp toàn bộ quy trình trở nên dễ quản lý hơn.
Ngoài ra, tôi sẽ đi vào các mẫu thiết kế dự án dbt cụ thể mà tôi dựa vào trong công việc hàng ngày. Những phương pháp này đã chứng tỏ giá trị trong nỗ lực xây dựng các nền tảng và kho dữ liệu chính xác, trực quan, dễ điều hướng và thân thiện với người dùng.
Áp dụng các cách tiếp cận này giúp việc tạo ra các nền tảng dữ liệu đáp ứng tiêu chuẩn chất lượng cao trở nên dễ dàng hơn đồng thời giảm thiểu rủi ro tiềm ẩn, cuối cùng dẫn đến các dự án dựa trên dữ liệu thành công hơn.
dbt là gì?
dbt (Data Build Tool) là một giải pháp mã nguồn mở mạnh mẽ được thiết kế riêng cho mô hình hóa dữ liệu, tận dụng các mẫu SQL và hàm ref() (tham chiếu) để thiết lập mối quan hệ giữa các thực thể trong cơ sở dữ liệu như bảng, view, schema và nhiều hơn nữa. Tính linh hoạt của nó rất phù hợp với nguyên tắc DRY (Do Not Repeat Yourself - Không lặp lại chính mình).
Với dbt, bạn có thể tạo một mẫu SQL duy nhất có thể tái sử dụng và dễ dàng thích ứng với các môi trường dữ liệu khác nhau. Khi mẫu được viết xong, nó có thể được “biên dịch” để tạo ra các truy vấn SQL cần thiết cho việc thực thi trong từng môi trường cụ thể.
Cách tiếp cận của dbt giúp nâng cao hiệu quả và đảm bảo tính nhất quán giữa các giai đoạn của pipeline dữ liệu, giảm dư thừa và lỗi tiềm ẩn đồng thời đơn giản hóa việc bảo trì và mở rộng hạ tầng dữ liệu.
Mô hình hóa dữ liệu đóng vai trò trung tâm trong kỹ thuật dữ liệu, và dbt là một công cụ tuyệt vời. Thực tế, tôi cho rằng làm chủ dbt là điều hoàn toàn thiết yếu với bất kỳ ai muốn trở thành một chuyên gia dữ liệu thành công!
Hãy xem mẫu dbt dưới đây. Đây là định nghĩa một bảng đơn giản, nhưng đi kèm siêu dữ liệu cho biết người dùng nên sử dụng cơ sở dữ liệu và schema nào:
/*
models/example/table_a.sql
Welcome to your first dbt model!
Did you know that you can also configure models directly within SQL files?
This will override configurations stated in dbt_project.yml
Try changing "table" to "view" below
*/
{{ config(
materialized='table',
alias='table_a',
schema='events',
tags=["example"]
) }}
select
1 as id
, 'Some comments' as comments
union all
2 as id
, 'Some comments' as comments
Hãy tưởng tượng ở phía hạ nguồn của pipeline dữ liệu, chúng ta có một view sinh ra từ bảng (table_a.sql) vừa tạo ở trên. Vậy sơ đồ phả hệ pipeline dữ liệu của chúng ta sẽ như sau:

Ví dụ phả hệ pipeline dữ liệu. Hình do Tác giả cung cấp.
Chúng ta sẽ dùng hàm ref() để kết nối hai giai đoạn của pipeline; trong trường hợp này, table_b.sql có thể được định nghĩa như sau:
-- models/example/table_b.sql
-- Use the ref function to select from other models
{{ config(
materialized='view',
tags=["example"],
schema='events'
) }}
select *
from {{ ref('table_a') }}
where id = 1
Hàm ref() cho biết mô hình table_b đứng sau table_a (hạ nguồn). Giờ đây, chúng ta có thể chạy toàn bộ pipeline chỉ với một lệnh dbt: dbt run --select tag:example.
Nhờ khả năng tái sử dụng mã, dbt cung cấp các tính năng khiến nó trở thành công cụ tuyệt vời để quản lý và tối ưu hóa quy trình công việc dữ liệu trên nhiều môi trường (sản xuất, phát triển, kiểm thử, v.v.).
Bên cạnh đó, một trong những khả năng cốt lõi của dbt là tự động tạo tài liệu SQL toàn diện, điều này giúp cải thiện đáng kể tính minh bạch và giúp các nhà phát triển dữ liệu cũng như các bên liên quan trong kinh doanh dễ hiểu hơn về các mô hình dữ liệu.
Một trong những thách thức trong lĩnh vực mô hình hóa dữ liệu là xây dựng các pipeline chuyển đổi SQL phức tạp với nhiều lớp trong khi vẫn làm cho mã của bạn có thể tái sử dụng. Những pipeline này đòi hỏi suy nghĩ cẩn trọng và kiểm thử tỉ mỉ để đảm bảo hoạt động hiệu quả và duy trì tính minh bạch trong tổ chức để mọi người đều hiểu được logic. dbt cũng có thể giúp ở khía cạnh này.
Ngoài ra, dbt hỗ trợ kiểm thử chất lượng dữ liệu và kiểm thử đơn vị cho logic SQL, cho phép bạn xác thực độ chính xác và độ tin cậy của các chuyển đổi theo cách có cấu trúc và tự động (quy trình CI/CD).
Một tính năng quan trọng khác là tự động hóa linh hoạt nhờ macro, cho phép tạo các đoạn mã có thể tùy biến và tái sử dụng, hợp lý hóa các tác vụ phức tạp và nâng cao năng suất.
Những chức năng kết hợp này khiến dbt trở thành giải pháp lý tưởng cho mọi tác vụ liên quan đến SQL và các môi trường dữ liệu, từ đảm bảo tính toàn vẹn dữ liệu đến tự động hóa các quy trình lặp lại, đồng thời duy trì hiệu quả và khả năng mở rộng.
Hãy bắt tay thực hành và chạy một vài ví dụ với dbt và BigQuery như một nền tảng dữ liệu!
Cài đặt và thiết lập dbt
Trong hướng dẫn này, chúng ta sẽ dùng Google Cloud BigQuery làm giải pháp kho dữ liệu. Tầng miễn phí khiến nó trở thành lựa chọn lý tưởng để học tập. Bạn có thể kích hoạt BigQuery trong tài khoản Google Cloud của mình.
Chúng ta sẽ cài đặt dbt cục bộ bằng Python và trình quản lý pip, tạo môi trường ảo, rồi bắt đầu chạy các mô hình và kiểm thử mẫu.
Chạy các lệnh sau trong dòng lệnh của bạn:
pip install virtualenv
mkdir dbt
cd dbt
virtualenv dbt_env -p python3.9
source dbt_env/bin/activate
pip install -r requirements.txt
Tệp requirements.txt của chúng ta nên chứa các phụ thuộc sau:
dbt-core==1.8.6
dbt-bigquery==1.8.2
dbt-extractor==0.5.1
dbt-semantic-interfaces==0.5.1
Sau đó, trong phần còn lại của hướng dẫn, chúng ta sẽ thực hiện như sau:
- Dùng tài khoản dịch vụ của dự án Google để kết nối dbt với BigQuery.
- Tạo các mô hình mẫu và chạy chúng.
- Thêm kiểm thử chất lượng dữ liệu và kiểm thử đơn vị cho các mô hình.
- Tạo tài liệu.
Hãy tạo thông tin xác thực tài khoản dịch vụ cho ứng dụng dbt của chúng ta:
- Đi tới bảng điều khiển IAM của Google Cloud và trong phần “Service Accounts”, tạo một tài khoản dịch vụ mới với quyền BigQuery Admin:

Tạo tài khoản dịch vụ cho BigQuery trong Google Cloud. Hình do Tác giả cung cấp.
- Tạo khóa JSON mới cho tài khoản dịch vụ và lưu trữ ở nơi an toàn, vì bạn sẽ cần nó sau này:

Lưu khóa riêng của tài khoản dịch vụ ở định dạng JSON. Hình do Tác giả cung cấp.
- Bây giờ, hãy chạy lệnh
dbt inittrong terminal để khởi tạo dự án dbt của chúng ta.
- Làm theo lời nhắc để chỉ định thiết lập dự án dbt của bạn và xác định đường dẫn tới thông tin xác thực tài khoản dịch vụ BigQuery.
Sau khi hoàn tất thiết lập, bạn sẽ nhận được thông báo như sau:
19:18:45 Profile my_dbt written to /Users/mike/.dbt/profiles.yml using target's profile_template.yml and your supplied values. Run 'dbt debug' to validate the connection.
Và cấu trúc thư mục sẽ tương tự như sau:
.
├── my_dbt
│ ├── README.md
│ ├── analyses
│ ├── dbt_project.yml
│ ├── macros
│ ├── models
│ ├── polybox-data-dev.json
│ ├── seeds
│ ├── snapshots
│ └── tests
├── dbt_env
│ ├── bin
│ ├── lib
│ └── pyvenv.cfg
├── logs
│ └── dbt.log
├── readme.md
└── requirements.txt
Chúng ta thấy rằng profiles.yml đã được tạo trong thư mục gốc của máy cục bộ, nhưng lý tưởng là chúng ta muốn nó nằm trong thư mục ứng dụng, vậy hãy di chuyển nó.
cd my_dbt
touch profiles.yml
Cuối cùng, hãy điều chỉnh nội dung profiles.yml để phản ánh tên dự án và thêm thông tin xác thực tài khoản dịch vụ Google:
my_dbt:
target: dev
outputs:
dev:
type: bigquery
method: service-account-json
project: dbt_bigquery_dev # replace with your-bigquery-project-name
dataset: source
threads: 4 # Must be a value of 1 or greater
# [OPTIONAL_CONFIG](#optional-configurations): VALUE
# These fields come from the service account json keyfile
keyfile_json:
type: service_account
project_id: your-bigquery-project-name-data-dev
private_key_id: bd709bd92708a38ae33abbff0
private_key: "-----BEGIN PRIVATE KEY-----\nMIIEv...
...
...
...q8hw==\n-----END PRIVATE KEY-----\n"
client_email: some@your-bigquery-project-name-data-dev.iam.gserviceaccount.com
client_id: 1234
auth_uri: https://accounts.google.com/o/oauth2/auth
token_uri: https://oauth2.googleapis.com/token
auth_provider_x509_cert_url: https://www.googleapis.com/oauth2/v1/certs
client_x509_cert_url: https://www.googleapis.com/robot/v1/metadata/x509/educative%40bq-shakhomirov.iam.gserviceaccount.com
Vậy là xong! Chúng ta sẵn sàng biên dịch dự án.
- Chạy lệnh này trong dòng lệnh:
export DBT_PROFILES_DIR='.'
dbt compile
Kết quả đầu ra sẽ giống như sau:
(dbt_env) mike@MacBook-Pro my_dbt % dbt compile
19:47:32 Running with dbt=1.8.6
19:47:33 Registered adapter: bigquery=1.8.2
19:47:33 Unable to do partial parsing because saved manifest not found. Starting full parse.
19:47:34 Found 2 models, 4 data tests, 479 macros
19:47:34
19:47:35 Concurrency: 4 threads (target='dev')
Thiết lập dự án ban đầu của chúng ta đã hoàn tất.
Cấu trúc dự án dbt
Chúng ta muốn thiết kế dự án dbt một cách tiện lợi và minh bạch để phản ánh rõ kiến trúc kho dữ liệu.
Tôi khuyến nghị sử dụng template và macro trong dự án dbt của bạn, đồng thời đưa vào các tên cơ sở dữ liệu tùy chỉnh để tách biệt hiệu quả các môi trường dữ liệu thành sản xuất, phát triển và kiểm thử.
Cách tiếp cận này cải thiện tổ chức và giảm thiểu rủi ro vô tình thay đổi ở sai môi trường, từ đó tăng cường quản trị dữ liệu và sự ổn định của quy trình công việc. Bằng cách này, chúng ta có thể dễ dàng quản lý và duy trì các môi trường đó, giúp đảm bảo dữ liệu sản xuất luôn an toàn và không bị ảnh hưởng bởi những thay đổi thử nghiệm hay kiểm thử.
Cơ sở dữ liệu ở các môi trường khác nhau cũng có thể được đặt tên một cách có cấu trúc và nhất quán bằng các hậu tố phù hợp (_prod, _dev, _test), giúp phân biệt môi trường dễ dàng hơn và hỗ trợ chuyển đổi, triển khai trơn tru.

Các lớp của kho dữ liệu trong môi trường sản xuất. Hình do Tác giả cung cấp.
Ví dụ, chúng ta có thể đưa các lớp mô hình dữ liệu chính vào quy ước đặt tên cơ sở dữ liệu bằng cách dùng tiền tố raw_ và base_ trong tên cơ sở dữ liệu:
Schema/Dataset Tanle
RAW_DEV SERVER_DB_1 -- mocked data
RAW_DEV SERVER_DB_2 -- mocked data
RAW_DEV EVENTS -- mocked data
RAW_PROD SERVER_DB_1 -- real production data from pipelines
RAW_PROD SERVER_DB_2 -- real production data from pipelines
RAW_PROD EVENTS -- real production data from pipelines
...
BASE_PROD EVENTS -- enriched data
BASE_DEV EVENTS -- enriched data
...
ANALYTICS_PROD REPORTING -- materialized queries and aggregates
ANALYTICS_DEV REPORTING
ANALYTICS_PROD AD_HOC -- ad-hoc queries and views
Để chèn các tên cơ sở dữ liệu tùy chỉnh này một cách linh hoạt, bạn chỉ cần tạo một macro dbt xử lý tự động. Bằng cách tận dụng phương pháp này, bạn có thể đảm bảo các tên cơ sở dữ liệu chính xác được sử dụng trong môi trường phù hợp mà không phải chỉnh sửa cấu hình thủ công mỗi lần.
Hãy xem đoạn mã dưới đây, chứa một macro đặt các schema khác nhau tùy theo môi trường chúng ta đang ở:
-- cd my_dbt
-- ./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 -%}
{{ custom_schema_name | trim }}
{%- endif -%}
{%- endmacro %}
Giờ đây, bất cứ khi nào chúng ta biên dịch các mô hình, dbt sẽ tự động áp dụng tên cơ sở dữ liệu tùy chỉnh dựa trên cấu hình trong thiết lập của từng mô hình. Điều này có nghĩa là tên cơ sở dữ liệu phù hợp sẽ được chèn trong quá trình biên dịch, đảm bảo các mô hình khớp với môi trường tương ứng — sản xuất, phát triển hoặc kiểm thử.
Bằng cách tích hợp tính năng này, chúng ta loại bỏ nhu cầu thay đổi thủ công tên cơ sở dữ liệu, qua đó nâng cao hiệu quả và độ chính xác của quy trình làm việc.
Các cấu hình cần thiết này có thể được đặt trong properties.yml cho các mô hình:
# my_dbt/models/example/properties.yml
version: 2
models:
- name: table_a
config:
description: "A starter dbt model"
schema: |
{%- if target.name == "dev" -%} raw_dev
{%- elif target.name == "prod" -%} raw_prod
{%- elif target.name == "test" -%} raw_test
{%- else -%} invalid_database
{%- endif -%}
columns:
- name: id
description: "The primary key for this table"
tests:
- unique
- not_null
- name: table_b
config:
description: "A starter dbt model"
schema: |
{%- if target.name == "dev" -%} analytics_dev
{%- elif target.name == "prod" -%} analytics_prod
{%- elif target.name == "test" -%} analytics_test
{%- else -%} invalid_database
{%- endif -%}
columns:
- name: id
description: "The primary key for this table"
tests:
- unique
- not_null
Như bạn thấy, chúng ta có thể dùng các câu lệnh điều kiện cơ bản để đưa logic vào tệp cấu hình, nhờ dbt hỗ trợ Jinja.
Macro không thể dùng trong ngữ cảnh này, nhưng chúng ta có thể dùng các điều kiện đơn giản bằng biểu thức Jinja trong các tệp .yml. Chúng cần được đặt trong dấu ngoặc kép. Điều này đảm bảo ngôn ngữ template được diễn giải đúng khi thực thi.
Hãy chạy lệnh dbt compile và xem điều gì xảy ra:
(dbt_env) mike@Mikes-MacBook-Pro my_dbt % dbt compile -s table_b -t prod
18:43:43 Running with dbt=1.8.6
18:43:44 Registered adapter: bigquery=1.8.2
18:43:44 Unable to do partial parsing because config vars, config profile, or config target have changed
18:43:45 Found 2 models, 480 macros
18:43:45
18:43:46 Concurrency: 4 threads (target='prod')
18:43:46
18:43:46 Compiled node 'table_b' is:
-- Use the ref function to select from other models
select *
from dbt_bigquery_dev.raw_prod.table_a
where id = 1
Làm việc với biến trong dbt
dbt hỗ trợ biến, đây là tính năng tùy biến rất mạnh. Biến có thể dùng trong cả mẫu SQL và macro, và có thể truyền từ dòng lệnh như sau:
dbt run -m table_b -t dev --vars '{my_var: my_value}'
Biến phải được khai báo trong tệp dự án chính dbt_project.yml. Ví dụ, đoạn dưới đây minh họa cách làm:
name: 'my_dbt'
version: '1.0.0'
config-version: 2
...
...
...
# In this example config, we tell dbt to build all models in the example/
# directory as views. These settings can be overridden in the individual model
# files using the {{ config(...) }} macro.
models:
polybox_dbt:
# Config indicated by + and applies to all files under models/example/
example:
# +materialized: view
# schema: |
# {%- if target.name == "dev" -%} analytics_dev_mike
# {%- elif target.name == "prod" -%} analytics_prod
# {%- elif target.name == "test" -%} analytics_test
# {%- else -%} invalid_database
# {%- endif -%}
vars:
my_var: ""
Hãy dùng biến để tạo tên bảng tùy chỉnh (tên bí danh) trong dbt.
Nếu không có alias, tên gốc của mô hình (tên tệp) sẽ được dùng làm alias theo mặc định. Logic đơn giản này đảm bảo mô hình được tham chiếu bằng alias đã cấu hình hoặc tên mặc định, tùy theo thiết lập. Cách triển khai như sau, đảm bảo tính linh hoạt trong cách đặt và tham chiếu tên mô hình giữa các môi trường:
-- get_custom_alias.sql
{% macro generate_alias_name(custom_alias_name=none, node=none) -%}
{%- if custom_alias_name -%}
{{ custom_alias_name | trim }}
{%- elif node.version -%}
{{ return(node.name ~ "_v" ~ (node.version | replace(".", "_"))) }}
{%- else -%}
{{ node.name }}
{%- endif -%}
{%- endmacro %}
Hãy ghi đè hành vi này bằng biến. Đây là thiết lập phổ biến cho nhà phát triển dữ liệu để giảm rủi ro “dẫm chân” nhau khi làm việc trên staging (phát triển).
Chúng ta muốn thêm tên nhà phát triển vào tất cả thực thể cơ sở dữ liệu (bảng, view, v.v.) do kỹ sư phát triển tạo ra.
Hãy tạo macro generate_alias_name.sql:
--my_dbt/macros/generate_alias_name.sql
{% macro generate_alias_name(custom_alias_name=none, node=none) -%}
{% set apply_alias_suffix = var('apply_alias_suffix') %}
{%- if custom_alias_name -%}
{{ custom_alias_name }}{{ apply_alias_suffix | trim }}
{%- elif node.version -%}
{{ return(node.name ~ "_v" ~ (node.version | replace(".", "_"))) }}
{%- else -%}
{{ node.name }}{{ apply_alias_suffix | trim }}
{%- endif -%}
{%- endmacro %}
Đừng quên thêm biến mới vào dbt_project.yml và chạy lệnh này trong dòng lệnh:
$ dbt compile -m table_b -t dev --vars '{apply_alias_suffix: _mike}'
Bạn sẽ thấy đầu ra như sau:
08:58:06 Running with dbt=1.8.6
08:58:07 Registered adapter: bigquery=1.8.2
08:58:07 Unable to do partial parsing because config vars, config profile, or config target have changed
08:58:07 Unable to do partial parsing because a project config has changed
08:58:08 Found 2 models, 481 macros
08:58:08
08:58:08 Concurrency: 4 threads (target='dev')
08:58:08
08:58:08 Compiled node 'table_b' is:
-- Use the ref function to select from other models
select *
from bigquery-data-dev.raw_dev.table_a_mike
where id = 1
Chúng ta thấy biến đã được thêm vào tên bảng: table_a_mike.
Các lớp mô hình dữ liệu
Phần này nói về cách chúng ta thiết kế kho dữ liệu theo khía cạnh chuyển đổi dữ liệu. Cấu trúc dự án logic, đơn giản hóa trong dbt có thể trông như dưới đây:
.
└── models
└── some_data_source
├── _data_source_model__docs.md
├── _data_source__models.yml
├── _sources.yml -- raw data table declarations
└── base -- base transformations, e.g. JSON to cols
| ├── base_transactions.sql
| └── base_orders.sql
└── analytics -- deeply enriched data prod grade data, QA'ed
├── _analytics__models.yml
├── some_model.sql
└── some_other_model.sql
Về cá nhân, tôi luôn cố gắng giữ lớp mô hình dữ liệu nền tảng (base) sạch và đơn giản nhất có thể, đảm bảo chỉ áp dụng chuyển đổi dữ liệu khi thực sự cần. Với cách tiếp cận này, chúng ta hướng tới việc thiết kế và triển khai lớp dữ liệu base_ với mức can thiệp tối thiểu ở cấp độ cột.
Tuy nhiên, đôi khi một mức độ điều chỉnh nhất định có thể hữu ích, đặc biệt khi tối ưu hiệu năng truy vấn. Trong các trường hợp đó, những điều chỉnh nhỏ ở lớp base có thể cải thiện hiệu quả đáng kể, nên cần cân bằng giữa sự đơn giản và lợi ích hiệu năng. Khi đó, việc thêm một phép join phụ hoặc bộ lọc phân vùng là hợp lý.
Một thực hành khuyến nghị là áp dụng các kỹ thuật sau để nâng cao mô hình dữ liệu và pipeline của bạn:
- Khi cần thiết, sử dụng materialization bền vững và clustering cho các đối tượng ở lớp cuối
biz_vàmart_. Điều này có thể giúp cải thiện hiệu năng và đảm bảo logic nghiệp vụ được quản lý hiệu quả. - Tránh dùng Google Sheets làm nguồn dữ liệu. Hạn chế trong xử lý tập dữ liệu lớn có thể gây ra sự không nhất quán và nút thắt hiệu năng.
- Tôi khuyến nghị dùng cập nhật incremental với clustering và điều kiện incremental.
- Tránh mẫu
select *và cân nhắc tách các tệp SQL dài, phức tạp thành các mô hình nhỏ hơn có kiểm thử đơn vị. - Cố gắng không dùng dbt seeds, vì chúng chỉ hỗ trợ tệp CSV và không lý tưởng để nạp dữ liệu vào bảng trong cơ sở dữ liệu của bạn.
- Thay vào đó, hãy cân nhắc seed các bảng kiểm thử thông qua materialization tùy chỉnh. Ví dụ, một truy vấn SQL có thể tạo ra đầu ra được tham chiếu trong các mô hình khác. Cách này đảm bảo các bảng của bạn được thể hiện đúng trong đồ thị phả hệ dữ liệu, mang lại khả năng quan sát và theo dõi tốt hơn trong hạ tầng dữ liệu.
Xem truy vấn SQL dưới đây. Nó giải thích cách tạo materialization tùy chỉnh như vậy:
-- my_dbt/macros/operation.sql
{%- materialization operation, default -%}
{%- set identifier = model['alias'] -%}
{%- set target_relation = api.Relation.create(
identifier=identifier, schema=schema, database=database,
type='table') -%}
-- ... setup database ...
-- ... run pre-hooks...
-- build model
{% call statement('main') -%}
{{ run_sql_as_simple_script(target_relation, sql) }}
{%- endcall %}
-- ... run post-hooks ...
-- ... clean up the database...
-- COMMIT happens here
{{ adapter.commit() }}
-- Return the relations created in this materialization
{{ return({'relations': [target_relation]}) }}
{%- endmaterialization -%}
-- my_dbt/macros/operation_helper.sql
{%- macro run_sql_as_simple_script(relation, sql) -%}
{{ log("Creating table " ~ relation) }}
{{ sql }}
{%- endmacro -%}
Giờ nếu thêm một mô hình table_c để minh họa tính năng này, chúng ta có thể dùng SQL sau:
-- my_dbt/models/example/table_c.sql
{{ config(
materialized='operation',
tags=["example"]
) }}
create or replace table {{this.database}}.{{this.schema}}.{{this.name}} (
id int64
,comments string
);
insert into {{this.database}}.{{this.schema}}.{{this.name}} (id, comments)
select
1 as id
, 'Some comments' as comments
union all
select
2 as id
, 'Some comments' as comments
;
Giờ nếu chúng ta biên dịch, nó sẽ giống một script SQL:
$ dbt compile -m table_c -t dev
Đầu ra:
10:45:24 Running with dbt=1.8.6
10:45:25 Registered adapter: bigquery=1.8.2
10:45:25 Found 3 models, 483 macros
10:45:25
10:45:26 Concurrency: 4 threads (target='dev')
10:45:26
10:45:26 Compiled node 'table_c' is:
-- Use the ref function to select from other models
create or replace table bigquery-data-dev.source.table_c (
id int64
,comments string
);
insert into bigquery-data-dev.source.table_c (id, comments)
select
1 as id
, 'Some comments' as comments
union all
select
2 as id
, 'Some comments' as comments
;
Lợi ích của cách tiếp cận này là chúng ta không còn cần phụ thuộc vào adapter BigQuery nữa. Nếu tạo thêm một bảng hoặc view tham chiếu tới thao tác này, ta chỉ việc dùng hàm ref() tiêu chuẩn.
table_c sẽ tự động được nhận diện như một phụ thuộc trong phả hệ dữ liệu. Điều này giúp dễ dàng theo dõi mối quan hệ giữa các bảng và đảm bảo các mối quan hệ giữa các mô hình được ghi lại đầy đủ trong môi trường dữ liệu của bạn.
Phương pháp này giúp quản lý phụ thuộc và cung cấp cái nhìn rõ ràng về luồng dữ liệu qua các giai đoạn khác nhau, bao gồm các bước xử lý phức tạp liên quan đến script. Điều này đặc biệt hữu ích khi duy trì các pipeline dữ liệu phức tạp.

DAG (đồ thị có hướng không chu trình) trong dbt hiển thị các phụ thuộc của table_b. Hình do Tác giả cung cấp
Giờ chúng ta chỉ cần thêm table_c vào pipeline:
-- models/example/table_b.sql
{{ config(
tags=["example"]
) }}
select *
from {{ ref('table_a') }}
where id = 1
union all
select *
from {{ ref('table_c') }}
where id = 2
-- select 1;
Tài liệu sẽ tự động được tạo nếu chúng ta chạy các lệnh sau trong dòng lệnh!
dbt docs generate
dbt docs serve
Một ví dụ nâng cao hơn về dự án kho dữ liệu trong dbt có thể có cấu trúc như dưới đây. Nó bao gồm nhiều nguồn dữ liệu và các chuyển đổi qua nhiều lớp mô hình (stg, base, mrt, biz) để cuối cùng tạo ra các mô hình data mart.
└── models
├── int -- only if required and 100% necessary for reusable logic
│ └── finance
│ ├── _int_finance__models.yml
│ └── int_payments_pivoted_to_orders.sql
├── marts -- deeply enriched, QAed data with complex transformations
│ ├── finance
│ │ ├── _finance__models.yml
│ │ ├── orders.sql
│ │ └── payments.sql
│ └── marketing
│ ├── _marketing__models.yml
│ └── customers.sql
└── src (or staging) -- raw data with basic transformations applied
├── some_data_source
│ ├── _data_source_model__docs.md
│ ├── _data_source__models.yml
│ ├── _sources.yml
│ └── base
│ ├── base_transactions.sql
│ └── base_orders.sql
└── another_data_source
├── _data_source_model__docs.md
├── _data_source__models.yml
├── _sources.yml
└── base
├── base_marketing.sql
└── base_events.sql
Kiểm thử đơn vị cho logic mô hình
Kiểm thử đơn vị là bước quan trọng trong quy trình pipeline dữ liệu, nơi chúng ta có thể chạy kiểm thử để xác thực logic đằng sau các mô hình dữ liệu. Cũng như bạn sẽ kiểm thử đơn vị cho các hàm Python để đảm bảo chúng hoạt động như mong đợi, tôi áp dụng cách tiếp cận tương tự cho việc kiểm thử các mô hình dữ liệu.
Bằng cách chạy các kiểm thử này, chúng ta có thể phát hiện sớm các vấn đề tiềm ẩn và đảm bảo các chuyển đổi cùng logic hoạt động chính xác. Thực hành này giúp duy trì chất lượng dữ liệu và ngăn lỗi lan truyền qua pipeline, là yếu tố thiết yếu trong quy trình kỹ thuật dữ liệu.
Chúng ta có thể thêm một kiểm thử đơn vị cho mô hình bằng cách chỉnh sửa tệp properties.yml:
# my_dbt/models/example/properties.yml
version: 2
models:
...
unit_tests: # dbt test --select "table_b,test_type:unit"
- name: test_table_b
description: "Check my table_b logic captures all records from table_a and table_c."
model: table_b
given:
- input: ref('table_a')
rows:
- {id: 1, comments: 'Some comments'}
- {id: 2, comments: 'Some comments'}
- input: ref('table_c')
rows:
- {id: 1, comments: 'Some comments'}
- {id: 2, comments: 'Some comments'}
expect:
rows:
- {id: 1, comments: 'Some comments'}
- {id: 2, comments: 'Some comments'}
Giờ nếu chúng ta chạy lệnh dbt test trong dòng lệnh, chúng ta có thể thực thi các kiểm thử đơn vị:
% dbt test --select "table_b,test_type:unit"
Đây là đầu ra:
11:33:05 Running with dbt=1.8.6
11:33:06 Registered adapter: bigquery=1.8.2
11:33:06 Unable to do partial parsing because config vars, config profile, or config target have changed
11:33:07 Found 3 models, 483 macros, 1 unit test
11:33:07
11:33:07 Concurrency: 4 threads (target='dev')
11:33:07
11:33:07 1 of 1 START unit_test table_b::test_table_b ................................... [RUN]
11:33:12 1 of 1 PASS table_b::test_table_b .............................................. [PASS in 5.12s]
11:33:12
11:33:12 Finished running 1 unit test in 0 hours 0 minutes and 5.77 seconds (5.77s).
11:33:12
11:33:12 Completed successfully
11:33:12
Hãy thử đổi id của hàng trong expect thành 3, chúng ta sẽ nhận lỗi cho cùng bài kiểm thử đó:
11:33:28 Completed with 1 error and 0 warnings:
11:33:28
11:33:28 Failure in unit_test test_table_b (models/example/properties.yml)
11:33:28
actual differs from expected:
@@ ,id,comments
,1 ,Some comments
+++,2 ,Some comments
---,3 ,Some comments
Kiểm thử chất lượng dữ liệu
dbt cũng hỗ trợ kiểm tra chất lượng dữ liệu. Trước đây tôi đã viết về chủ đề này trong bài blog hợp đồng dữ liệu. Chúng ta có thể kiểm tra hầu như mọi yếu tố liên quan tới chất lượng dữ liệu, ví dụ độ mới của dữ liệu, điều kiện hàng, tính hạt, v.v.
Hãy xem kỹ hơn mô hình table_b. Nó đã có một số kiểm tra dữ liệu sẵn:
- name: table_b
config:
description: "A starter dbt model"
schema: |
{%- if target.name == "dev" -%} analytics_dev
{%- elif target.name == "prod" -%} analytics_prod
{%- elif target.name == "test" -%} analytics_test
{%- else -%} invalid_database
{%- endif -%}
columns:
- name: id
description: "The primary key for this table"
tests:
- unique
- not_null
Ở đây, dưới phần định nghĩa tests, chúng ta kiểm tra trường table_b.id đã materialize với điều kiện unique và not_null. Để chạy bài kiểm thử cụ thể này, dùng lệnh sau:
dbt test -s table_b
Chúng ta cũng có thể kiểm tra tính toàn vẹn tham chiếu cho bộ dữ liệu. Điều này rất quan trọng khi làm việc với các mô hình dữ liệu có join, vì nó đảm bảo các mối quan hệ giữa thực thể được duy trì chính xác. Các kiểm thử này giúp xác định cách các thực thể khác nhau, như bảng hoặc cột, liên hệ với nhau.
Ví dụ, xem đoạn mã dbt dưới đây, minh họa cách mỗi refunds.refund_id được liên kết với một transactions.id hợp lệ. Ánh xạ này đảm bảo mọi khoản hoàn tiền đều gắn với giao dịch hợp lệ, duy trì tính toàn vẹn dữ liệu và ngăn bản ghi mồ côi hay mối quan hệ không nhất quán trong các mô hình dữ liệu của bạn:
- name: refunds
enabled: true
description: An incremental table
columns:
- name: refund_id
tests:
- relationships:
tags: ['relationship']
to: ref('transactions')
field: id
Yêu cầu dữ liệu thường bao gồm việc đặt kỳ vọng về thời điểm dữ liệu mới phải sẵn sàng và quy định độ trễ tối đa cho các bản cập nhật. Các kiểm tra này rất quan trọng để đảm bảo dữ liệu vẫn phù hợp để phân tích (cập nhật kịp thời).
Trong dbt, điều này có thể triển khai bằng cách sử dụng kiểm thử độ mới (freshness), cho phép bạn giám sát liệu dữ liệu mới có đến trong khung thời gian kỳ vọng hay không.
Ví dụ, bạn có thể cấu hình một kiểm thử độ mới để xác minh rằng bản ghi gần nhất trong một bảng đáp ứng tiêu chí độ mới đã định. Điều này đảm bảo pipeline dữ liệu của bạn cung cấp bản cập nhật kịp thời và nhất quán, giúp duy trì độ tin cậy và chính xác của dữ liệu đồng thời đáp ứng các yêu cầu nhạy cảm về thời gian.
Xem đoạn mã dưới đây. Nó giải thích cách thiết lập kiểm thử độ mới trong dbt:
# example model
- name: orders
enabled: true
description: A source table declaration
tests:
- dbt_utils.recency: # https://github.com/dbt-labs/dbt-utils#recency-source
tags: ['freshness']
datepart: day
field: timestamp
interval: 1
Tất cả các kiểm thử dbt này thật tuyệt vời và rất hữu ích trong công việc hằng ngày của kỹ sư dữ liệu! Chúng giúp duy trì kho dữ liệu tốt và pipeline dữ liệu nhất quán.
Kết luận
Xây dựng giải pháp kho dữ liệu là một nhiệm vụ phức tạp đòi hỏi lập kế hoạch và tổ chức cẩn thận. dbt, với vai trò là công cụ template, giúp thực hiện điều đó một cách nhất quán.
Trong bài viết này, tôi đã phác thảo một số kỹ thuật tổ chức thư mục chuyển đổi dữ liệu dbt để tăng tính rõ ràng và khả năng cộng tác. Bằng cách lưu trữ tệp SQL theo cấu trúc hợp lý, chúng ta tạo ra một môi trường dễ khám phá, ngay cả với người mới tham gia dự án.
DBT cung cấp nhiều tính năng để đơn giản hóa quy trình hơn nữa. Ví dụ, chúng ta có thể làm giàu các mẫu SQL bằng cách đưa vào các đoạn mã tái sử dụng thông qua macro, biến và hằng số. Theo kinh nghiệm của tôi, khi kết hợp với thực hành hạ tầng như mã (infrastructure-as-code), chức năng này giúp thực thi đúng quy trình CI/CD, tăng tốc đáng kể phát triển và triển khai.
Nếu bạn muốn nâng cao kiến thức dbt, hãy cân nhắc tham gia khóa Introduction to dbt trên DataCamp. Đây là nguồn tài nguyên tuyệt vời có thể giúp bạn bắt đầu thành công với nhiều thực hành trực quan hơn!
FAQs
Tôi có thể dùng dbt với bất kỳ cơ sở dữ liệu nào không?
dbt hoạt động với nhiều kho dữ liệu và cơ sở dữ liệu đám mây như Snowflake, BigQuery, Redshift và PostgreSQL. Mỗi cơ sở dữ liệu được hỗ trợ đều có adapter riêng, bạn cần cài đặt riêng.
Tôi có cần kỹ năng lập trình để dùng dbt không?
Kiến thức SQL cơ bản là cần thiết vì dbt tập trung vào các chuyển đổi dựa trên SQL. Kỹ năng nâng cao như Python có thể tăng cường chức năng dbt, đặc biệt khi bạn làm việc với phần mở rộng dbt hoặc tự động hóa quy trình.
dbt chỉ dành cho kho dữ liệu đám mây thôi sao?
Mặc dù dbt thường được dùng với các kho dữ liệu dựa trên đám mây, nó cũng hoạt động với cơ sở dữ liệu on-premises. Tuy nhiên, các kho dữ liệu đám mây cung cấp khả năng mở rộng tốt hơn, và dbt phát huy hiệu quả trong bối cảnh đó.
dbt có thể tích hợp vào quy trình CI/CD không?
Có! dbt được thiết kế để hoạt động trong pipeline CI/CD, hỗ trợ tự động hóa, kiểm thử và quản lý phiên bản. Các công cụ như GitHub Actions hoặc Jenkins có thể tích hợp dbt vào quy trình CI/CD, đảm bảo kiểm thử và triển khai mô hình dữ liệu vững chắc.
dbt có dùng cho dữ liệu streaming được không?
dbt chủ yếu được thiết kế cho xử lý theo lô thay vì dữ liệu streaming. Tuy nhiên, với nhu cầu gần thời gian thực, bạn có thể kết hợp dbt với các mô hình incremental cập nhật dữ liệu theo chu kỳ, tùy theo yêu cầu độ mới.
Sự khác nhau giữa dbt Cloud và dbt Core là gì?
dbt Core là phiên bản miễn phí, mã nguồn mở của dbt, bạn có thể chạy cục bộ hoặc trong môi trường đám mây của riêng bạn. dbt Cloud là dịch vụ quản lý, bao gồm các tính năng bổ sung như giao diện người dùng, lập lịch job và tích hợp Git, hữu ích cho cộng tác nhóm.
dbt có làm việc với các chuyển đổi không phải SQL không?
dbt tập trung vào các chuyển đổi SQL, nên các chuyển đổi không phải SQL không được hỗ trợ gốc. Tuy vậy, tính mô-đun của dbt cho phép tích hợp với công cụ bên ngoài, và bạn có thể bao gồm script tùy chỉnh nếu cần xử lý phức tạp hơn.
Đam mê và tập trung vào chuyển đổi số, tôi hứng khởi trước những thách thức của tiếp thị số.
Trước khi chuyển đến Vương quốc Anh, tôi đã có hơn mười năm kinh nghiệm trong lĩnh vực bán hàng, rủi ro ngân hàng doanh nghiệp và tiếp thị số, qua đó phát triển chuyên môn về quản trị rủi ro, mô hình hóa toán học, phân tích thống kê, quản trị kinh doanh và marketing.
Sau khi hoàn thành chương trình MBA tại Newcastle, hiện tôi mong muốn theo đuổi sự nghiệp trong lĩnh vực tiếp thị dựa trên dữ liệu, khoa học máy tính hoặc AI, với tiềm năng tiếp tục học lên tiến sĩ. Những lĩnh vực này đem lại cơ hội ứng dụng khoa học vào thực tiễn, phát triển nghề nghiệp liên tục, đổi mới và đóng góp cho một ngành công nghiệp năng động.
