dbt nam 2026: Chuyển đổi Dữ liệu, Kiểm thử và Câu hỏi Phỏng vấn
Hướng dẫn dbt cho kỹ sư dữ liệu: chuyển đổi SQL, mô hình phân lớp, chiến lược incremental, kiểm thử dữ liệu và câu hỏi phỏng vấn kỹ thuật với các ví dụ mã cho năm 2026.

dbt (data build tool) đã trở thành framework chuẩn để chuyển đổi dữ liệu bên trong các data warehouse hiện đại. Tính đến năm 2026, hơn 40.000 công ty sử dụng dbt trong môi trường production. Bài hướng dẫn này đề cập đến các cơ chế cốt lõi của việc chuyển đổi trong dbt, chiến lược kiểm thử, và những câu hỏi thường xuất hiện trong các buổi phỏng vấn data engineering.
dbt xử lý chữ T trong ELT. Nó biên dịch các câu lệnh SQL SELECT thành DDL (CREATE TABLE, CREATE VIEW, MERGE) và thực thi chúng trên warehouse — Snowflake, BigQuery, Redshift, hoặc Databricks. Quản lý phiên bản, phân giải phụ thuộc, kiểm thử và tài liệu được tích hợp sẵn.
Mô hình Phân lớp: Staging, Intermediate và Marts
Một dự án dbt được cấu trúc tốt sẽ phân tách các phep chuyển đổi thành ba lớp. Cách tiếp cận này, được cộng đồng dbt phổ biến, áp dụng nguyên tắc single-responsibility cho mỗi model và giúp việc gỡ lỗi trở nên đơn giản.
Staging là lớp gần nhất với dữ liệu thô. Nhiệm vụ của lớp này rất hẹp: đổi tên cột, ép kiểu dữ liệu, và lọc các hàng không hợp lệ. Không join, không aggregation.
-- models/staging/stg_orders.sql
WITH source AS (
SELECT * FROM {{ source('ecommerce', 'raw_orders') }}
)
SELECT
id AS order_id,
customer_id,
CAST(order_date AS DATE) AS order_date,
CAST(amount AS DECIMAL(10, 2)) AS order_amount,
LOWER(status) AS order_status
FROM source
WHERE id IS NOT NULLIntermediate áp dụng logic nghiệp vụ — join, aggregation, window function. Các model này tham chiếu đến staging model thông qua ref(), hàm này đăng ký các phụ thuộc trong DAG.
-- models/intermediate/int_customer_orders.sql
WITH orders AS (
SELECT * FROM {{ ref('stg_orders') }}
),
customers AS (
SELECT * FROM {{ ref('stg_customers') }}
)
SELECT
c.customer_id,
c.customer_name,
COUNT(o.order_id) AS total_orders,
SUM(o.order_amount) AS lifetime_value,
MIN(o.order_date) AS first_order_date,
MAX(o.order_date) AS last_order_date
FROM customers c
LEFT JOIN orders o ON c.customer_id = o.customer_id
GROUP BY c.customer_id, c.customer_nameMarts là lớp tiêu thụ cuối cùng — các bảng sạch, có tài liệu, mà dashboard và analyst truy vấn trực tiếp.
Cách tiếp cận phân lớp này đảm bảo rằng một nguồn dữ liệu bị hỏng chỉ ảnh hưởng đến staging, không phải toàn bộ pipeline. Mỗi lớp có thể được kiểm thử độc lập.
Hàm ref() và Phân giải DAG
ref() không phải là một alias đơn giản. Khi gọi {{ ref('stg_orders') }}, hai việc xảy ra: nó phân giải tên bảng đầy đủ tại thời điểm biên dịch, và nó đăng ký một cạnh phụ thuộc trong directed acyclic graph (DAG) của dbt. Không có ref(), dbt không có cách nào xác định thứ tự thực thi.
Một lỗi phổ biến là viết trực tiếp tên bảng thay vì sử dụng ref(). Điều này phá vỡ việc theo dõi phụ thuộc và có thể dẫn đến việc các model chạy trước khi các phụ thuộc upstream sẵn sàng.
-- Sai: tham chiếu hardcode, không có theo dõi phụ thuộc
SELECT * FROM analytics.stg_orders
-- Đúng: ref() đăng ký phụ thuộc
SELECT * FROM {{ ref('stg_orders') }}DAG cũng hỗ trợ các tính năng như dbt run --select stg_orders+, chạy một model và tất cả model phía downstream — hữu ích cho việc rebuild có mục tiêu sau khi thay đổi schema nguồn.
Materialization: Chọn Chiến lược Lưu trữ Phù hợp
dbt cung cấp bốn materialization tích hợp sẵn, mỗi loại phù hợp với các mẫu truy cập và khối lượng dữ liệu khác nhau.
| Materialization | Lưu trữ | Phù hợp cho | Đánh đổi |
|----------------|---------|----------|----------|
| view | Không lưu (tính khi đọc) | Biến đổi nhẹ, dataset nhỏ | Đọc chậm với dữ liệu lớn |
| table | Tái tạo bảng hoàn toàn mỗi lần chạy | Model lớp mart, đọc nhanh | Tái tạo toàn bộ, chi phí cao |
| incremental | Chỉ thêm/merge hàng mới | Bảng fact lớn, event stream | Logic phức tạp hơn, cần unique_key |
| ephemeral | Inline như CTE, không bao giờ được lưu | Logic tái sử dụng giữa các model | Không thể truy vấn trực tiếp |
Mặc định là view. Ghi đè trong khối config của model hoặc trong dbt_project.yml:
-- models/marts/fct_daily_revenue.sql
{{
config(
materialized='incremental',
unique_key='revenue_date',
incremental_strategy='merge'
)
}}
SELECT
DATE(order_date) AS revenue_date,
SUM(order_amount) AS daily_revenue,
COUNT(DISTINCT customer_id) AS unique_customers
FROM {{ ref('stg_orders') }}
WHERE order_status = 'completed'
{% if is_incremental() %}
AND order_date > (SELECT MAX(revenue_date) FROM {{ this }})
{% endif %}
GROUP BY DATE(order_date)Khối is_incremental() chỉ chạy trong quá trình build incremental — khi full refresh (dbt run --full-refresh), khối này bị bỏ qua và toàn bộ bảng được xây dựng lại.
Sẵn sàng chinh phục phỏng vấn Data Engineering?
Luyện tập với mô phỏng tương tác, flashcards và bài kiểm tra kỹ thuật.
Kiểm thử Chất lượng Dữ liệu Ở Mỗi Lớp
dbt cung cấp hai loại kiểm thử: generic tests khai báo trong YAML, và singular tests viết dưới dạng file SQL độc lập trả về các hàng khi thất bại.
Generic tests bao gồm các ràng buộc cấu trúc:
# models/staging/_stg_models.yml
version: 2
models:
- name: stg_orders
columns:
- name: order_id
tests:
- unique # Không có order ID trùng lặp
- not_null # Mọi hàng đều có order ID
- name: order_status
tests:
- accepted_values:
values: ['completed', 'pending', 'cancelled', 'refunded']
- name: customer_id
tests:
- not_null
- relationships: # Kiểm tra tính toàn vẹn tham chiếu
to: ref('stg_customers')
field: customer_idSingular tests xác thực các quy tắc nghiệp vụ mà generic tests không thể biểu đạt:
-- tests/assert_revenue_never_negative.sql
-- Trả về các hàng có doanh thu hàng ngày âm (kỳ vọng 0 hàng)
SELECT
revenue_date,
daily_revenue
FROM {{ ref('fct_daily_revenue') }}
WHERE daily_revenue < 0Unit tests, được giới thiệu trong dbt 1.8, xác thực logic chuyển đổi với đầu vào được kiểm soát và đầu ra mong đợi — không cần warehouse. Chúng chạy trực tiếp trong giai đoạn dbt test.
Chiến lược kiểm thử nên theo dòng chảy dữ liệu: kiểm tra freshness ở mức source, xác thực ràng buộc schema ở staging, toàn vẹn tham chiếu ở intermediate, và xác nhận logic nghiệp vụ ở mart.
dbt Core v1.10 và Fusion Engine
dbt Core v1.10 giới thiệu cờ --sample cho các lệnh run và build. Cờ này áp dụng lấy mẫu dựa trên thời gian cho ref() và source(), cho phép developer xác thực các phép chuyển đổi trên một tập con dữ liệu mà không tốn chi phí full build. Rất hữu ích khi làm việc với các model được hỗ trợ bởi bảng fact chứa hàng tỷ hàng.
Fusion engine — viết lại hoàn toàn bằng Rust — hiện là mặc định cho các dự án mới trên dbt Cloud với Snowflake, BigQuery, Redshift và Databricks. Fusion giới thiệu semantic versioning bắt đầu từ 2.0 và mang lại cải thiện hiệu suất đáng kể cho quá trình biên dịch và thực thi.
Các bổ sung đáng chú ý khác trong 2026: đặc tả YAML cho Semantic Layer để định nghĩa metric tập trung, Cost Insights (beta) để ước tính chi phí tính toán warehouse mỗi model, và native private packages hiện đã khả dụng rộng rãi.
Macro và Jinja cho Logic Tái sử dụng
Khi cùng một mẫu SQL xuất hiện trong nhiều model, hãy trích xuất nó thành macro. Macro là các hàm Jinja được lưu trong thư mục macros/.
-- macros/cents_to_dollars.sql
{% macro cents_to_dollars(column_name) %}
ROUND(CAST({{ column_name }} AS DECIMAL(10, 4)) / 100, 2)
{% endmacro %}Gọi trong bất kỳ model nào:
-- models/staging/stg_payments.sql
SELECT
payment_id,
order_id,
{{ cents_to_dollars('amount_cents') }} AS amount_dollars,
payment_method
FROM {{ source('stripe', 'payments') }}Cách này giữ cho codebase tuân thủ nguyên tắc DRY. Phương án thay thế — sao chép công thức chuyển đổi vào mọi model cần nó — tạo ra gánh nặng bảo trì và sự không nhất quán.
Câu hỏi Phỏng vấn Thường Gặp
Các buổi phỏng vấn data engineering năm 2026 ngày càng kiểm tra kiến thức dbt vượt xa những điều cơ bản. Dưới đây là những câu hỏi phân biệt các ứng viên, được rút ra từ các mẫu thường thấy tại các công ty sử dụng modern data stack.
H: Một model incremental đã xử lý lại toàn bộ bảng qua đêm. Chuyện gì đã xảy ra?
Nguyên nhân phổ biến nhất: cột unique_key chứa giá trị null. Khi dbt cố gắng MERGE trên key null, việc khớp thất bại cho mọi hàng, dẫn đến insert trùng lặp. Nguyên nhân thứ hai: ai đó chạy dbt run --full-refresh mà không nhận ra rằng nó xây dựng lại bảng từ đầu. Thứ ba: bộ lọc is_incremental() tham chiếu một cột chưa tồn tại trong bảng đích (lần chạy đầu tiên so với các lần chạy tiếp theo). Việc gỡ lỗi bắt đầu bằng việc kiểm tra SQL đã biên dịch trong target/compiled/.
H: Kiểm thử nên được phân lớp như thế nào trong dự án dbt production?
Kiểm tra freshness của source chạy trước — nếu source chưa được cập nhật, các model downstream không nên chạy trên dữ liệu cũ. Kiểm thử staging xác thực ràng buộc schema: primary key (unique + not_null), giá trị chấp nhận, và ép kiểu. Kiểm thử intermediate kiểm tra toàn vẹn tham chiếu giữa các join. Kiểm thử mart xác nhận các bất biến nghiệp vụ: revenue >= 0, active users >= paying users, và tổng các phần bằng tổng thể. Tất cả test chạy trong CI trên pull request đối với schema dev, sau đó chạy lại sau khi triển khai trong production với cảnh báo.
H: Khi nào một model nên là ephemeral so với view?
Model ephemeral inline SQL của nó như một CTE vào các model tiêu thụ — hữu ích cho logic tái sử dụng không cần truy vấn độc lập. View tính toán khi đọc và tồn tại như đối tượng có thể truy vấn. Đánh đổi: model ephemeral không thể kiểm thử trực tiếp (không có bảng để chạy assertion) và không xuất hiện trong các data lineage tool. Sử dụng ephemeral cho logic helper nội bộ; sử dụng view cho bất cứ thứ gì cần kiểm thử hoặc giám sát độc lập.
H: Giải thích sự khác biệt giữa source() và ref().
source() trỏ đến các bảng thô được định nghĩa trong file sources.yml — đây là các bảng mà dbt không quản lý. ref() trỏ đến các model dbt khác. Cả hai đều đăng ký phụ thuộc DAG, nhưng source() còn cho phép kiểm tra freshness (dbt source freshness), điều mà ref() không làm. Sử dụng source() cho bảng thô và ref() cho mọi thứ khác không phải là tùy chọn — đó là cách dbt biết những gì nó kiểm soát và những gì nó không kiểm soát.
Để có bộ câu hỏi phỏng vấn data engineering rộng hơn, bao gồm các chủ đề như kiến trúc pipeline ETL và ELT, các module luyện tập SharpSkill về dbt fundamentals và advanced dbt patterns đề cập các khái niệm này với các bài tập tương tác.
Bắt đầu luyện tập!
Kiểm tra kiến thức với mô phỏng phỏng vấn và bài kiểm tra kỹ thuật.
Kết luận
- dbt chuyển đổi dữ liệu thô trong warehouse thông qua các câu lệnh SQL SELECT, được biên dịch tự động thành DDL — cấu trúc phân lớp staging/intermediate/mart giữ cho mỗi model tập trung vào một trách nhiệm duy nhất
- Hàm
ref()là xương sống của quản lý phụ thuộc: phân giải tên bảng và xây dựng DAG điều khiển thứ tự thực thi - Materialization incremental giảm chi phí cho bảng lớn, nhưng đòi hỏi xử lý cẩn thận
unique_key, giá trị null, và bộ lọcis_incremental() - Kiểm thử nên theo dòng chảy dữ liệu — freshness source, ràng buộc schema ở staging, toàn vẹn tham chiếu ở intermediate, xác nhận logic nghiệp vụ ở mức mart
- dbt Core v1.10 mang đến cờ
--samplecho việc lặp nhanh hơn, trong khi Fusion engine (dựa trên Rust, phiên bản 2.0) hiện là mặc định trên dbt Cloud - Câu hỏi phỏng vấn về dbt tập trung vào việc gỡ lỗi các thất bại thực tế (xử lý lại incremental, null key, source cũ) chứ không phải định nghĩa — kinh nghiệm thực tế với SQL đã biên dịch và CI pipeline quan trọng hơn câu trả lời thuộc lòng
Bắt đầu luyện tập!
Kiểm tra kiến thức với mô phỏng phỏng vấn và bài kiểm tra kỹ thuật.
Thẻ
Chia sẻ
Bài viết liên quan

ETL vs ELT năm 2026: Kiến trúc Data Pipeline và So sánh Chi tiết
So sánh chi tiết giữa ETL và ELT trong data engineering. Tìm hiểu kiến trúc data pipeline, ưu điểm và nhược điểm của từng phương pháp, và cách chọn giải pháp phù hợp năm 2026.

Top 25 Câu Hỏi Phỏng Vấn Data Engineering Năm 2026
25 câu hỏi phỏng vấn data engineering được hỏi nhiều nhất năm 2026, bao gồm SQL, data pipeline, ETL/ELT, Spark, Kafka, data modeling và system design kèm lời giải chi tiết.

Apache Spark 4: Tính Năng Mới và Structured Streaming - Hướng Dẫn Phỏng Vấn
Khám phá các tính năng mới trong Apache Spark 4 bao gồm ANSI SQL mode, VARIANT data type, Real-Time Mode streaming và Spark Connect. Hướng dẫn chi tiết với ví dụ code và câu hỏi phỏng vấn.