DE

[Tech Blog #36 | Data Engineering] dbt (Data Build Tool) 101: Cách Mạng Hóa Tầng Biến Đổi Dữ Liệu Bằng SQL Chuẩn Chuyên Nghiệp

0 views
[Tech Blog #36 | Data Engineering] dbt (Data Build Tool) 101: Cách Mạng Hóa Tầng Biến Đổi Dữ Liệu Bằng SQL Chuẩn Chuyên Nghiệp

1. Đặt vấn đề: "Cơn ác mộng" của Stored Procedures và các file SQL rời rạc

Hãy nhớ lại cách thức biến đổi dữ liệu truyền thống trong các kho dữ liệu (Data Warehouse):

  • Đội ngũ kỹ sư viết hàng chục file SQL dài hàng nghìn dòng, hoặc tạo các Stored Procedures phức tạp nhúng sâu trong cơ sở dữ liệu.
  • Các câu lệnh SQL này chứa đầy những đoạn mã DDL thủ công: CREATE TABLE IF NOT EXISTS..., DROP TABLE..., INSERT INTO....

Khi hệ thống phình to lên, quy trình này biến thành một "cơn ác mộng":

  1. Không thể kiểm soát phiên bản (No Version Control): Không ai biết ai đã sửa câu lệnh Stored Procedure trên database vào lúc nào, tại sao lại sửa và sửa cái gì (không thể dùng Git hiệu quả).
  2. Mù quáng về nguồn gốc dữ liệu (No Data Lineage): Bảng fact_monthly_revenue lấy dữ liệu từ bảng nào? Nếu sửa bảng staging_orders, những báo cáo nào ở hạ nguồn (downstream) sẽ bị sập? Không ai dám chắc!
  3. Không có kiểm thử tự động (No Testing): Dữ liệu bị NULL, bị trùng khóa chính (duplicate key), hoặc số tiền bị âm... cứ thế âm thầm lọt vào báo cáo của Ban Giám đốc mà không hề có tiếng chuông cảnh báo nào reo lên.

Để giải quyết triệt để những nỗi đau này, phong trào Analytics Engineering đã ra đời cùng với công cụ thống trị toàn cầu: dbt (Data Build Tool).


2. dbt là gì? Triết lý "Tất cả những gì bạn viết chỉ là câu lệnh SELECT"

dbt là một công cụ mã nguồn mở chuyên đảm nhận chữ T (Transform) trong mô hình hiện đại ELT (Extract - Load - Transform) (như đã học ở [Tech Blog #12]).

Triết lý cốt lõi của dbt gói gọn trong 2 nguyên tắc vàng:

  1. Bạn chỉ cần viết câu lệnh SELECT thuần túy: Bạn hoàn toàn không cần bận tâm viết CREATE TABLE, CREATE VIEW, hay viết code quản lý transaction. dbt sẽ tự động làm toàn bộ việc đó thay bạn.
  2. Áp dụng tư duy Công nghệ phần mềm vào Dữ liệu (Software Engineering for Data): Toàn bộ logic biến đổi dữ liệu được quản lý bằng Git (Version Control), có kiểm thử tự động (Testing), có tài liệu tự sinh (Auto-generated Documentation), và có quy trình CI/CD chuyên nghiệp.
[Dữ liệu thô trong Data Warehouse: raw_orders, raw_customers]
                           │
                           ▼ (Viết file .sql chứa lệnh SELECT)
+───────────────────────────────────────────────────────────+
| dbt (Data Build Tool)                                     |
| 1. Quản lý phụ thuộc tự động qua hàm ref('...')           |
| 2. Tự sinh đồ thị dòng chảy dữ liệu (Data Lineage)        |
| 3. Tự động kiểm thử tính toàn vẹn (Unique, Not Null...)   |
+───────────────────────────────────────────────────────────+
                           │
                           ▼ (Tự động biên dịch & thực thi)
[Bảng sạch hoàn chỉnh: dim_customers, fact_orders sẵn sàng cho BI]

3. Bốn chiến lược hiện thực hóa bảng (Materializations) trong dbt

Khi viết một model SQL trong dbt, bạn chỉ cần cấu hình dòng materialized ở đầu file, dbt sẽ quyết định cách lưu trữ nó dưới cơ sở dữ liệu:

Kiểu MaterializationCách dbt tạo ra trong DatabaseƯu điểmNhược điểmKhi nào nên dùng?
viewCREATE VIEW AS SELECT...Chạy tức thì, không tốn dung lượng ổ đĩa, dữ liệu luôn tươi mới 100%.Truy vấn chậm nếu logic phức tạp hoặc quét dữ liệu lớn.Tầng làm sạch sơ bộ (Staging models), dữ liệu nhỏ.
tableCREATE TABLE AS SELECT...Tốc độ truy vấn báo cáo siêu nhanh (dữ liệu đã tính sẵn nằm trên đĩa).Tốn thời gian tạo lại toàn bộ bảng mỗi lần chạy dbt run.Tầng phục vụ BI (Data Mart / Fact table) quy mô vừa.
incrementalINSERT INTO... / MERGE...Vũ khí tối thượng cho Big Data: Chỉ quét và nạp dữ liệu mới của ngày hôm nay, không tính lại lịch sử.Logic viết phức tạp hơn một chút (cần kiểm tra mốc thời gian).Các bảng log sự kiện, bảng giao dịch hàng trăm triệu dòng.
ephemeralKhông tạo gì cả, chỉ là một CTE tạm thời.Không làm rác database.Không thể dùng công cụ BI truy vấn trực tiếp vào nó.Các bước tính toán trung gian chỉ dùng cho 1 model duy nhất.

4. Bảng so sánh: Stored Procedures truyền thống vs. dbt

Tiêu chíStored Procedures / Script SQL rời rạcdbt (Data Build Tool)
Quản lý mã nguồn (Git)🔴 Kém (Mã nhúng sâu trong DB hoặc file lẻ tẻ).🟢 Tuyệt đối: Toàn bộ project là code chuẩn Git, dễ review pull request.
Quản lý thứ tự thực thi (DAG)🔴 Thủ công, dễ bị chạy sai thứ tự nếu không nhớ.🟢 Tự động 100%: Chỉ cần dùng hàm ref('model_name'), dbt tự dựng cây DAG.
Kiểm thử chất lượng dữ liệu🔴 Viết code kiểm tra thủ công rất mệt mỏi.🟢 Khai báo cực dễ: Chỉ cần gõ unique, not_null trong file YAML.
Tài liệu & Sơ đồ Lineage🔴 Vẽ tay bằng Excel hoặc sơ đồ giấy, nhanh chóng lỗi thời.🟢 Tự động sinh website tài liệu và sơ đồ dòng chảy dữ liệu đồ họa trực quan.

5. Thực hành thực chiến: Xây dựng một dbt Model hoàn chỉnh kèm Testing

Dưới đây là một ví dụ thực tế về cấu trúc 1 dbt model làm sạch đơn hàng và file khai báo kiểm thử tự động:

File 1: Model làm sạch dữ liệu models/marts/fact_daily_orders.sql

-- 1. Cấu hình chiến lược lưu trữ là TABLE (hoặc INCREMENTAL)
{{ config(
    materialized='table',
    schema='analytics'
) }}

-- 2. Dùng hàm ref() để dbt tự động hiểu bảng này phụ thuộc vào 2 bảng tiền trạm (Staging)
WITH orders AS (
    SELECT * FROM {{ ref('stg_orders') }}
),

customers AS (
    SELECT * FROM {{ ref('stg_customers') }}
)

-- 3. Chỉ viết câu lệnh SELECT nghiệp vụ thuần túy, KHÔNG CẦN viết CREATE TABLE!
SELECT 
    o.order_id,
    o.customer_id,
    c.full_name,
    c.city,
    o.order_date,
    o.order_amount,
    
    -- Chuẩn hóa trạng thái đơn hàng
    CASE 
        WHEN o.status IN ('PAID', 'SHIPPED') THEN 'COMPLETED'
        WHEN o.status = 'CANCELLED' THEN 'CANCELLED'
        ELSE 'PENDING'
    END AS clean_status

FROM 
    orders o
    LEFT JOIN customers c ON o.customer_id = c.customer_id
WHERE 
    o.order_date >= '2026-01-01';

File 2: Khai báo kiểm thử tự động models/marts/schema.yml

Chỉ bằng vài dòng YAML đơn giản, bạn thiết lập được "hàng rào bảo vệ" chất lượng dữ liệu:

version: 2

models:
  - name: fact_daily_orders
    description: "Bảng Fact tổng hợp đơn hàng hàng ngày đã được làm sạch để phục vụ BI."
    columns:
      - name: order_id
        description: "Khóa chính của đơn hàng."
        tests:
          - unique:          # CẢNH BÁO NẾU BỊ TRÙNG KHÓA CHÍNH
              severity: error
          - not_null:        # CẢNH BÁO NẾU CÓ DÒNG NÀO BỊ RỖNG NULL
              severity: error

      - name: customer_id
        description: "Khóa ngoại liên kết tới bảng khách hàng."
        tests:
          - not_null
          - relationships:   # KIỂM TRA TÍNH TOÀN VẸN THAM CHIẾU (Foreign Key Integrity)
              to: ref('stg_customers')
              field: customer_id

      - name: clean_status
        description: "Trạng thái đơn hàng sau chuẩn hóa."
        tests:
          - accepted_values: # CHỈ CHẤP NHẬN 3 GIÁ TRỊ NÀY, NẾU LỌT CHỮ LẠ SẼ BÁO LỖI NGAY
              values: ['COMPLETED', 'CANCELLED', 'PENDING']

Khi bạn chạy lệnh:

dbt run   # dbt tự động biên dịch câu lệnh SELECT thành bảng thật trong CSDL
dbt test  # dbt tự động chạy toàn bộ các phép kiểm tra logic ở trên, bắt trọn 100% dữ liệu bẩn!

6. Câu hỏi phỏng vấn thực tế (Interview Q&A)

Câu 1 (Mức độ: Entry / Junior)

Câu hỏi: "Hàm ref() trong dbt đóng vai trò gì? Điều gì sẽ xảy ra nếu bạn hardcode trực tiếp tên bảng (ví dụ: FROM analytics.stg_orders) thay vì dùng ref('stg_orders')?"

Gợi ý trả lời:
*"Hàm ref() là 'trái tim' của dbt, đảm nhiệm 2 nhiệm vụ cốt lõi:

  1. Tự động xây dựng đồ thị phụ thuộc (DAG & Data Lineage): Nhờ hàm ref(), dbt biết chính xác model nào phụ thuộc vào model nào để sắp xếp thứ tự thực thi tuần tự (chạy bảng Staging trước, xong xuôi mới chạy bảng Fact).
  2. Tự động chuyển đổi môi trường (Environment Isolation): Khi lập trình viên chạy ở môi trường Dev, ref() sẽ tự động trỏ đến schema cá nhân của bạn (ví dụ dev_nam.stg_orders). Khi đưa lên Production, nó tự động trỏ đến schema prod.stg_orders.
    Nếu bạn hardcode trực tiếp tên bảng: dbt sẽ hoàn toàn bị mù quáng trước mối quan hệ phụ thuộc (không thể vẽ được sơ đồ Lineage và có thể chạy sai thứ tự), đồng thời bạn có nguy cơ ghi đè nhầm dữ liệu môi trường Production ngay khi đang test thử ở máy local!"*

Câu 2 (Mức độ: Junior / Mid-level)

Câu hỏi: "Chiến lược Materialization dạng incremental trong dbt hoạt động như thế nào? Tại sao nó là kỹ thuật sống còn cho Big Data?"

Gợi ý trả lời:
*"Trong một bảng có 500 triệu dòng giao dịch, việc dùng kiểu table thông thường (mỗi ngày xóa đi tính lại toàn bộ 500 triệu dòng) sẽ tiêu tốn lượng chi phí tính toán khổng lồ và mất hàng giờ đồng hồ.
Chiến lược incremental giải quyết bài toán này theo cơ chế:

  1. Ở lần chạy đầu tiên, dbt sẽ khởi tạo toàn bộ bảng như bình thường.
  2. Ở các lần chạy tiếp theo (dbt run), dbt tự động sinh ra câu lệnh INSERT INTO ... hoặc MERGE ..., sử dụng một điều kiện lọc ngày tháng (ví dụ: WHERE event_time > (SELECT MAX(event_time) FROM {{ this }})) để chỉ quét và nạp duy nhất phần dữ liệu mới phát sinh trong 24 giờ qua.
    Kỹ thuật này giúp rút ngắn thời gian chạy pipeline từ 2 tiếng xuống còn vài chục giây và tiết kiệm tới 90% chi phí quét dữ liệu trên các Cloud Data Warehouse như Snowflake, BigQuery hay ClickHouse."*

7. Key Takeaways (Ghi nhớ nhanh)

  1. dbt là tiêu chuẩn vàng của Analytics Engineering: Đưa tư duy phần mềm (Git, CI/CD, Testing) vào việc viết SQL kho dữ liệu.
  2. Bạn chỉ cần viết SELECT: Quên đi CREATE TABLE hay DROP VIEW, dbt sẽ tự động xử lý toàn bộ các thao tác DDL dưới nền.
  3. Luôn dùng ref('model') thay vì hardcode tên bảng: Để dbt tự động xây dựng đồ thị DAG dòng chảy dữ liệu (Lineage) và tự chuyển đổi môi trường Dev/Prod an toàn.
  4. Kiểm thử tự động (Testing) là bắt buộc: Chỉ cần vài dòng YAML để tự động bắt lỗi trùng khóa chính (unique) và dữ liệu rỗng (not_null) trước khi dữ liệu đến tay Ban Giám đốc.
0Claps