dbt 1 — Tổng quan & analytics engineering

13 thg 7, 2026 4 lượt xem
#sql
#data-engineering
#dbt
#elt
#analytics-engineering

dbt là gì — và không phải là gì

dbt (viết tắt của data build tool) là công cụ biến đổi dữ liệu (transform) đang là chuẩn de facto trong thế giới data hiện đại. Cần khắc sâu ngay một câu để tránh hiểu sai toàn bộ phần còn lại: dbt không di chuyển dữ liệu. dbt không đọc dữ liệu ra khỏi warehouse, không load dữ liệu vào warehouse, không kết nối tới hệ nguồn để hút dữ liệu. Tất cả những gì dbt làm là sinh ra các câu lệnh SQL và điều phối để warehouse tự chạy chúng.

Nói cách khác, dbt là chữ "T" trong ELT. Trong mô hình cũ ETL (Extract → Transform → Load), dữ liệu được hút ra, biến đổi ở một tầng trung gian (thường là một cụm xử lý riêng), rồi mới nạp vào kho. Trong mô hình mới ELT (Extract → Load → Transform), thứ tự đảo lại: dữ liệu thô được nạp thẳng vào warehouse trước (bằng Airbyte, Fivetran, CDC...), rồi mới biến đổi ngay bên trong warehouse. dbt đảm nhiệm đúng bước "Transform" cuối này. Sở dĩ ELT thắng thế là vì các warehouse phân tích hiện đại (BigQuery, Snowflake, Redshift, Databricks SQL, Postgres...) đã đủ mạnh và đủ rẻ để tự chạy các phép biến đổi khổng lồ — không cần một tầng transform riêng bên ngoài nữa.

Cơ chế cốt lõi rất đơn giản để hình dung: bạn viết một câu SELECT, dbt bọc nó thành một câu CREATE VIEW hoặc CREATE TABLE AS, gửi xuống warehouse thực thi, và bạn có một bảng/view mới chứa kết quả. Bạn không viết CREATE, DROP, INSERT, không quản lý DDL, không lo transaction hay việc bảng đã tồn tại hay chưa — dbt lo hết phần "thủ tục" đó. Bạn chỉ tập trung vào logic nghiệp vụ dưới dạng SQL SELECT.

Analytics engineering — triết lý nền tảng

dbt không chỉ là một công cụ; nó gắn liền với một cách làm việc gọi là analytics engineering. Ý tưởng trung tâm: mang toàn bộ thực hành tốt của kỹ thuật phần mềm vào công việc phân tích dữ liệu.

Trong nhiều năm, việc biến đổi dữ liệu cho báo cáo là một mớ hỗn độn: những câu SQL dài hàng nghìn dòng lưu rải rác trong công cụ BI, những "stored procedure" không ai dám sửa, logic trùng lặp copy-paste khắp nơi, không ai biết một con số trên dashboard được tính ra sao. Không có version control, không có test, không có tài liệu, không có review.

Analytics engineering đảo ngược tất cả những điều đó. Với dbt, các phép biến đổi trở thành code:

  • Version control: mọi model nằm trong Git — có lịch sử, có diff, có pull request, có review, có rollback.
  • Modularity (mô-đun hoá): tách logic thành các model nhỏ, mỗi model làm một việc, tái sử dụng lẫn nhau — thay vì một truy vấn khổng lồ.
  • Testing: khẳng định các giả định về dữ liệu (khoá không null, giá trị duy nhất, quan hệ tham chiếu hợp lệ) và để dbt kiểm tra tự động.
  • CI/CD: chạy test và build model tự động trên mỗi pull request trước khi merge.
  • Documentation & lineage: tài liệu và sơ đồ phụ thuộc sinh tự động từ chính code.

Vai trò analytics engineer ra đời từ đây: đứng giữa data engineer (lo hạ tầng, ingest, pipeline) và data analyst (lo phân tích, dashboard). Analytics engineer sở hữu tầng biến đổi — biến dữ liệu thô đã nạp vào warehouse thành các bảng sạch, đáng tin, có tài liệu để analyst dùng ngay. dbt chính là công cụ chủ lực của vai trò này.

Cách dbt hoạt động: compile rồi run

Đây là phần cần hiểu thật kỹ vì nó giải thích mọi thứ khác. Một model trong dbt là một file .sql chứa đúng một câu SELECT. Nhưng câu SELECT đó không phải SQL thuần — nó là SQL trộn với Jinja (một ngôn ngữ template) và tham chiếu tới các model/nguồn khác qua hai hàm đặc biệt:

  • ref('tên_model') — tham chiếu tới một model dbt khác.
  • source('tên_nguồn', 'tên_bảng') — tham chiếu tới một bảng nguồn thô (đã được khai báo trong file YAML).

Khi bạn chạy dbt run, dbt thực hiện hai bước tách bạch:

Bước 1 — Compile. dbt đọc từng file model, giải Jinja để biến template thành SQL thuần. Quan trọng nhất: khi gặp ref()source(), dbt thay chúng bằng tên bảng/schema thật trong warehouse, đồng thời ghi lại quan hệ phụ thuộc. Từ tập hợp các ref() này, dbt dựng nên một DAG (Directed Acyclic Graph — đồ thị có hướng không chu trình) mô tả model nào phụ thuộc model nào. SQL đã compile được ghi ra thư mục target/ để bạn có thể kiểm tra chính xác câu lệnh nào sẽ chạy.

Bước 2 — Run. dbt duyệt DAG theo đúng thứ tự phụ thuộc (topological order): model không phụ thuộc ai chạy trước, model phụ thuộc chúng chạy sau. Với mỗi model, dbt bọc câu SELECT đã compile vào CREATE VIEW/CREATE TABLE AS phù hợp và gửi xuống warehouse thực thi. dbt còn chạy song song những nhánh độc lập của DAG để tiết kiệm thời gian.

Điểm mấu chốt: mọi phép tính đều xảy ra TRÊN warehouse, không phải trên máy chạy dbt. Máy chạy dbt chỉ làm nhiệm vụ "biên dịch SQL và điều phối" — nó gửi lệnh và chờ warehouse trả về. Vì vậy dbt cực nhẹ và không cần tài nguyên tính toán lớn; sức mạnh nằm ở warehouse.

Các lệnh cốt lõi bạn sẽ dùng mỗi ngày:

  • dbt run — compile và tạo/cập nhật các model (view/table) trong warehouse.
  • dbt test — chạy các test dữ liệu đã khai báo.
  • dbt build — lệnh tổng hợp: với mỗi node theo thứ tự DAG, nó chạy model rồi chạy test cho model đó, đồng thời xử lý cả seed và snapshot. Đây là lệnh khuyến nghị cho production vì nó không cho dữ liệu "bẩn" lan xuống hạ nguồn — nếu test của một model fail, các model phụ thuộc nó sẽ bị bỏ qua.

Sơ đồ: chỗ đứng của dbt trong luồng ELT

Cần nhấn mạnh: dbt chỉ sống bên trong khối warehouse. Bước ingest (EL) do công cụ khác lo; bước tiêu thụ (BI) cũng do công cụ khác lo. dbt là tầng biến đổi ở giữa, và một orchestrator như Airflow thường đứng ngoài để lập lịch và kích hoạt cả hai bước theo đúng thời điểm.

dbt Core vs dbt Cloud

dbt có hai hình thức dùng, cùng chung lõi khái niệm:

dbt Core là công cụ mã nguồn mở, chạy dưới dạng CLI. Bạn cài đặt (thường qua pip), viết model trong editor bất kỳ, và chạy dbt run, dbt test, dbt build từ dòng lệnh. dbt Core hoàn toàn miễn phí. Nhưng nó chỉ là "engine" — nó không tự lập lịch, không có IDE, không host tài liệu. Bạn phải tự lo phần chạy định kỳ (thường bằng Airflow hoặc cron trên CI) và tự deploy nơi chạy nó.

dbt Cloud (nay còn gọi là dbt Platform) là dịch vụ được quản lý (managed) do dbt Labs cung cấp, bọc quanh lõi dbt và bổ sung: một IDE trên trình duyệt để viết và chạy model, một scheduler để chạy job theo lịch, CI tích hợp chạy trên pull request, hosting tài liệu và lineage, quản lý quyền và môi trường. dbt Cloud phù hợp với đội muốn hạ tầng "chìa khoá trao tay" thay vì tự dựng.

Cả hai đều làm việc với warehouse qua adapter — một plugin tương ứng với từng loại warehouse (dbt-bigquery, dbt-snowflake, dbt-redshift, dbt-postgres, dbt-databricks...). Adapter chịu trách nhiệm dịch các thao tác của dbt sang phương ngữ SQL và giao thức kết nối của warehouse cụ thể. Chọn adapter đúng warehouse là bước cấu hình đầu tiên của mọi project.

Lưu ý về hệ sinh thái: dbt Labs đang phát triển dbt Fusion, một engine viết bằng Rust có khả năng phân tích tĩnh SQL trước khi gửi xuống warehouse. Với mục đích của loạt bài này, mọi khái niệm nền tảng (model, ref, DAG, materialization) đều giống hệt bất kể engine — nên cứ nắm vững dbt Core 1.x là đủ.

Cấu trúc một dbt project

Một project dbt là một thư mục có bố cục quy ước. Các thành phần chính:

  • dbt_project.yml — file cấu hình gốc của project: tên project, đường dẫn các thư mục, và cấu hình mặc định cho model (ví dụ tầng nào materialize thành view, tầng nào thành table).
  • models/ — nơi chứa các file .sql model. Đây là trái tim của project.
  • seeds/ — các file CSV nhỏ, tĩnh (bảng tra cứu như mã quốc gia, ánh xạ danh mục) mà dbt nạp thẳng vào warehouse bằng dbt seed.
  • snapshots/ — định nghĩa cách "chụp" lịch sử thay đổi của một bảng theo thời gian (SCD Type 2), phục vụ nhu cầu "trước đây giá trị này là gì".
  • tests/ — các test SQL tuỳ biến (test dạng "singular") ngoài các test khai báo trong YAML.
  • macros/ — các đoạn Jinja tái sử dụng, giống hàm trong lập trình, để tránh lặp logic.

Về cách tổ chức models/, dbt khuyến nghị phân tầng ba lớp — đây là một trong những quy ước giá trị nhất của cộng đồng:

  1. staging — mỗi model staging tương ứng một bảng nguồn. Chỉ làm sạch nhẹ: đổi tên cột về chuẩn, ép kiểu, không join. Đây là "cửa ngõ" chuẩn hoá dữ liệu thô.
  2. intermediate — nơi chứa logic trung gian phức tạp: join nhiều staging, tính toán bước đệm. Không phục vụ trực tiếp người dùng cuối.
  3. marts — các bảng nghiệp vụ cuối cùng mà BI và analyst dùng: bảng fact và dimension, các bảng tổng hợp theo chủ đề (doanh thu, khách hàng...). Đây là "sản phẩm dữ liệu" đầu ra.

Nguyên tắc luồng chảy: dữ liệu đi staging → intermediate → marts, không bao giờ chảy ngược. Tầng càng cao càng gần nghiệp vụ, càng ổn định để chia sẻ.

Ví dụ: một model dùng ref()

Dưới đây là một model marts điển hình, join hai model staging để tạo bảng đơn hàng có thông tin khách hàng.

-- models/marts/fct_orders.sql  (minh hoạ)
with orders as (
    select * from {{ ref('stg_orders') }}
),

customers as (
    select * from {{ ref('stg_customers') }}
)

select
    o.order_id,
    o.order_date,
    o.amount,
    c.customer_id,
    c.customer_name,
    c.country
from orders o
left join customers c
    on o.customer_id = c.customer_id

Điều đáng chú ý: không có tên schema hay database thật nào trong câu SQL này. Chỉ có ref('stg_orders')ref('stg_customers'). Khi compile, dbt sẽ thay hai ref() này bằng tên bảng thật (ví dụ analytics.stg_orders), đồng thời biết rằng fct_orders phụ thuộc vào hai model kia — nên chúng phải được build trước. Nhờ vậy, đổi tên hay đổi schema của một model chỉ cần sửa ở một chỗ; toàn bộ hạ nguồn tự cập nhật theo.

Và đây là một dbt_project.yml rút gọn khai báo materialization mặc định theo tầng:

# dbt_project.yml  (minh hoạ)
name: 'analytics'
version: '1.0.0'
profile: 'analytics'

model-paths: ["models"]
seed-paths: ["seeds"]

models:
  analytics:
    staging:
      +materialized: view      # tầng staging: nhẹ, dựng lại tức thì
    intermediate:
      +materialized: view
    marts:
      +materialized: table     # tầng marts: vật chất hoá thành bảng cho BI đọc nhanh

QUY ƯỚC của sandbox: SQL sandbox của Knowledge Base là PostgreSQL, nên các câu SQL thuần có thể chạy thử ở đó. Nhưng code dbt (Jinja + YAML) ở trên KHÔNG chạy được trong sandbox — chúng cần engine dbt và một project thật để compile. Vì vậy mọi khối ref()/YAML ở đây đều là (minh hoạ). Chi tiết về materialized sẽ mổ xẻ ở bài Models & materializations.

DAG, lineage và tài liệu

Vì mọi phụ thuộc đều được khai báo qua ref(), dbt tự dựng đồ thị phụ thuộc (DAG) cho toàn bộ project mà bạn không phải khai báo thứ tự chạy thủ công. Đây là điểm khác biệt lớn so với việc tự viết một chuỗi script SQL: bạn chỉ cần nói "model này dùng model kia", dbt tự suy ra phải chạy cái nào trước.

DAG này mang lại ba lợi ích cùng lúc:

  • Sắp thứ tự tự động: dbt luôn build đúng thứ tự, chạy song song các nhánh độc lập.
  • Lineage (dòng dõi dữ liệu): dbt vẽ được sơ đồ "cột/bảng này bắt nguồn từ đâu, ảnh hưởng tới đâu". Cực kỳ quý khi debug hay đánh giá tác động của một thay đổi.
  • Chạy theo lát cắt: dùng cú pháp lựa chọn như dbt build --select fct_orders+ để chỉ chạy một model và mọi thứ phụ thuộc nó, thay vì chạy lại cả project.

Kèm theo đó, dbt docs generate sinh ra một site tài liệu tương tác từ chính code và các mô tả YAML: mỗi model có mô tả, danh sách cột, các test đang áp, và sơ đồ lineage bấm xem được. Tài liệu "sống" cùng code nên không bao giờ lỗi thời như tài liệu viết tay.

Vị trí trong modern data stack

dbt hiếm khi đứng một mình. Trong một modern data stack điển hình, dbt là mắt xích transform nối các mắt xích khác:

  • Ingest: Airbyte, Fivetran hoặc CDC (Debezium/GoldenGate) nạp dữ liệu thô vào warehouse.
  • Warehouse: BigQuery, Snowflake, Redshift... là nơi dbt chạy.
  • BI: Looker, Tableau, Metabase đọc các bảng marts do dbt tạo ra.
  • Orchestration: Airflow (hoặc dbt Cloud scheduler) kích hoạt cả pipeline theo lịch — thường theo trình tự "ingest xong → chạy dbt build → refresh BI". Xem Airflow — Tổng quan để hiểu cách một orchestrator gọi dbt.

Khi nào dùng dbt — và khi nào không

Dùng dbt khi phép biến đổi của bạn có thể diễn đạt bằng SQL và chạy trong warehouse. Đây là phần lớn công việc analytics: làm sạch, join, tổng hợp, dựng bảng fact/dimension, tính chỉ số nghiệp vụ. Nếu dữ liệu đã ở trong warehouse và bạn cần biến nó thành bảng có cấu trúc để báo cáo — dbt gần như luôn là lựa chọn đúng.

Không (hoặc chưa) dùng dbt khi:

  • Bạn cần xử lý ngoài phạm vi SQL: machine learning huấn luyện mô hình, xử lý ảnh/văn bản/nhị phân, thuật toán lặp phức tạp, biến đổi trên dữ liệu bán cấu trúc quá nặng. Những việc này thường thuộc về Spark hoặc code Python chuyên dụng — xem Spark — Tổng quan.
  • Bạn cần di chuyển dữ liệu giữa các hệ thống — đó là việc của công cụ ingest/EL, không phải dbt.
  • Bạn cần xử lý streaming độ trễ thấp theo từng sự kiện — dbt là công cụ batch theo model, không phải stream processor.

Ranh giới thực dụng: nếu bạn viết được nó thành một câu SELECT chạy trên warehouse thì để dbt làm; nếu không, đẩy nó cho công cụ chuyên xử lý và để dbt lo phần biến đổi SQL còn lại.

Use case thực tế

Một công ty thương mại điện tử có dữ liệu đơn hàng nằm ở Postgres ứng dụng, dữ liệu quảng cáo ở vài SaaS API, và log sự kiện web. Fivetran nạp tất cả vào BigQuery mỗi giờ dưới dạng bảng thô (raw). Đội analytics engineer dựng một dbt project:

  • Tầng staging chuẩn hoá từng nguồn: stg_orders, stg_customers, stg_ad_spend, stg_web_events — đổi tên cột về snake_case, ép kiểu ngày tháng, lọc bản ghi test.
  • Tầng intermediate join sự kiện web với đơn hàng để dựng phễu chuyển đổi.
  • Tầng marts cho ra fct_orders, dim_customers, và bảng tổng hợp mart_marketing_roi gộp chi phí quảng cáo với doanh thu.

Trên mỗi bảng, đội khai báo test: order_iduniquenot null, customer_id trong fct_orders phải tồn tại trong dim_customers (relationship test). Mỗi đêm, Airflow chờ Fivetran báo "đã nạp xong", rồi chạy dbt build. Nếu một test fail — ví dụ xuất hiện đơn hàng trùng khoá — dbt dừng nhánh đó lại, gửi cảnh báo, và các bảng hạ nguồn không bị nhiễm dữ liệu lỗi. Looker đọc thẳng các bảng marts, và số liệu trên dashboard giờ có một định nghĩa duy nhất, có tài liệu, có lineage truy ngược tới tận bảng thô.

Ghi nhớ

  • dbt = chữ T của ELT. Nó không di chuyển dữ liệu; nó sinh SQL và điều phối để warehouse tự chạy các phép biến đổi.
  • Model là file .sql chứa một câu SELECT; dbt lo phần DDL, materialization, thứ tự chạy.
  • ref()/source() vừa thay tên bảng thật khi compile, vừa dựng nên DAG phụ thuộc để dbt tự sắp thứ tự và vẽ lineage.
  • Vòng đời một lần chạy: compile (giải Jinja → SQL thuần + DAG) rồi run (tạo view/table theo thứ tự). dbt build = run + test + seed + snapshot theo DAG.
  • dbt Core (CLI, mã nguồn mở, tự lo lịch) vs dbt Cloud (IDE + scheduler + CI + hosting docs). Kết nối warehouse qua adapter.
  • Cấu trúc: dbt_project.yml, models/, seeds/, snapshots/, tests/, macros/; phân tầng staging → intermediate → marts.
  • analytics engineering = mang version control, test, CI/CD, modularity, docs vào phân tích.
  • Dùng dbt cho biến đổi SQL trong warehouse; việc ngoài SQL (ML, xử lý nặng phi-SQL) để cho Spark.

Đọc tiếp trong series

Liên quan: Airflow — Tổng quan · Mô hình hoá warehouse · Spark — Tổng quan

Bài viết liên quan

CSV/JSON/Parquet/Avro, lưu trữ theo hàng vs cột, nén, và OLTP vs OLAP.

13 thg 7, 2026 7

Data Engineering là gì, vai trò trong vòng đời dữ liệu, và bức tranh hệ sinh thái công cụ.

13 thg 7, 2026 5

Vì sao xử lý phân tán, mô hình Spark (RDD/DataFrame), lazy evaluation, shuffle và tối ưu.

13 thg 7, 2026 5

Đảm bảo chất lượng & minh bạch dữ liệu bằng dbt: data tests dựng sẵn (unique, not_null, relationships, accepted_values), singular test, unit test, và tài liệu tự sinh kèm lineage graph.

13 thg 7, 2026 5