Data Pipeline: Từ Batch đến Streaming trong hệ thống dữ liệu hiện đại

Posted on: 4/24/2026 6:14:55 AM

Trong thế giới dữ liệu hiện đại, một hệ thống xử lý dữ liệu hiệu quả là xương sống của mọi quyết định kinh doanh. Từ việc phân tích hành vi người dùng theo thời gian thực đến việc tổng hợp báo cáo doanh thu hàng ngày — tất cả đều phụ thuộc vào Data Pipeline. Bài viết này sẽ đi sâu vào kiến trúc Data Pipeline hiện đại, so sánh ETL vs ELT, Batch vs Streaming, và hướng dẫn chọn giải pháp phù hợp cho từng bài toán thực tế.

73% doanh nghiệp chuyển sang ELT (2026)
<1s độ trễ streaming pipeline
5x giảm chi phí với lakehouse
60% team dùng Unified Batch+Stream

Data Pipeline là gì?

Data Pipeline là một chuỗi các bước tự động hóa để di chuyển, biến đổi và tải dữ liệu từ nguồn (source) đến đích (destination). Pipeline có thể đơn giản như một script chạy hàng đêm để copy dữ liệu từ database production sang data warehouse, hoặc phức tạp như một hệ thống xử lý hàng triệu sự kiện mỗi giây từ IoT sensors.

graph LR
    A[Data Sources] --> B[Ingestion Layer]
    B --> C[Processing Layer]
    C --> D[Storage Layer]
    D --> E[Serving Layer]

    style A fill:#e94560,stroke:#fff,color:#fff
    style B fill:#2c3e50,stroke:#fff,color:#fff
    style C fill:#16213e,stroke:#fff,color:#fff
    style D fill:#2c3e50,stroke:#fff,color:#fff
    style E fill:#e94560,stroke:#fff,color:#fff

Kiến trúc tổng quan của Data Pipeline

ETL vs ELT: Hai triết lý khác nhau

Sự khác biệt giữa ETL (Extract-Transform-Load) và ELT (Extract-Load-Transform) không chỉ là thứ tự các bước — nó phản ánh hai triết lý hoàn toàn khác nhau về cách tiếp cận dữ liệu.

ETL — Cách tiếp cận truyền thống

Extract → Transform → Load

Dữ liệu được trích xuất từ nguồn, biến đổi (lọc, chuẩn hóa, aggregate) trên một server trung gian, rồi mới được tải vào kho dữ liệu đích. Mô hình này phổ biến trong kỷ nguyên data warehouse on-premise khi compute và storage đắt đỏ — bạn chỉ muốn lưu trữ dữ liệu đã "sạch".

graph LR
    S1[MySQL] --> E[Extract]
    S2[API] --> E
    S3[CSV Files] --> E
    E --> T[Transform Server]
    T --> L[Load]
    L --> DW[Data Warehouse]

    style T fill:#e94560,stroke:#fff,color:#fff
    style DW fill:#2c3e50,stroke:#fff,color:#fff
    style E fill:#f8f9fa,stroke:#e94560,color:#2c3e50
    style L fill:#f8f9fa,stroke:#e94560,color:#2c3e50
    style S1 fill:#f8f9fa,stroke:#2c3e50,color:#2c3e50
    style S2 fill:#f8f9fa,stroke:#2c3e50,color:#2c3e50
    style S3 fill:#f8f9fa,stroke:#2c3e50,color:#2c3e50

Luồng xử lý ETL truyền thống

ELT — Cách tiếp cận hiện đại

Extract → Load → Transform

Dữ liệu thô được tải thẳng vào hệ thống lưu trữ (data warehouse hoặc lakehouse), sau đó biến đổi tại chỗ bằng sức mạnh compute của chính hệ thống đích. Cách tiếp cận này tận dụng khả năng scale-out của các nền tảng cloud như BigQuery, Snowflake hay Databricks.

graph LR
    S1[MySQL] --> E[Extract]
    S2[API] --> E
    S3[CSV Files] --> E
    E --> L[Load Raw Data]
    L --> DW[Data Warehouse / Lakehouse]
    DW --> T[Transform với dbt / SQL]
    T --> DW

    style DW fill:#e94560,stroke:#fff,color:#fff
    style T fill:#2c3e50,stroke:#fff,color:#fff
    style E fill:#f8f9fa,stroke:#e94560,color:#2c3e50
    style L fill:#f8f9fa,stroke:#e94560,color:#2c3e50
    style S1 fill:#f8f9fa,stroke:#2c3e50,color:#2c3e50
    style S2 fill:#f8f9fa,stroke:#2c3e50,color:#2c3e50
    style S3 fill:#f8f9fa,stroke:#2c3e50,color:#2c3e50

Luồng xử lý ELT hiện đại — transform ngay tại data warehouse

Tiêu chí ETL ELT
Vị trí Transform Server trung gian (staging) Ngay tại data warehouse
Tốc độ ingestion Chậm hơn (phải transform trước) Nhanh hơn (load trước, transform sau)
Linh hoạt Khó thay đổi logic transform Dễ thay đổi — chỉ viết lại SQL/dbt model
Chi phí compute Tự quản lý server transform Dùng compute của cloud DW (pay-per-query)
Raw data Mất sau khi transform Luôn giữ nguyên bản gốc
Công cụ phổ biến Informatica, Talend, SSIS dbt, Fivetran, Airbyte + Snowflake/BigQuery
Phù hợp On-premise, compliance nghiêm ngặt Cloud-native, analytics hiện đại

💡 Xu hướng 2026

ELT đã trở thành chuẩn mực cho hầu hết dự án analytics mới. Với dbt (data build tool) làm lớp transformation chuẩn, team data chỉ cần viết SQL để định nghĩa business logic — version control, testing, documentation đều được dbt xử lý tự động.

Batch Processing — Xử lý theo lô

Batch processing là mô hình xử lý dữ liệu theo từng "lô" (batch) tại những thời điểm định trước — hàng giờ, hàng ngày, hoặc hàng tuần. Đây vẫn là backbone của hầu hết hệ thống analytics vì tính đơn giản, chi phí thấp và khả năng xử lý lượng dữ liệu lớn.

Khi nào dùng Batch?

  • Báo cáo doanh thu cuối ngày, cuối tháng
  • Training ML model định kỳ
  • Data warehouse refresh
  • Tổng hợp metrics (DAU, MAU, retention)
  • Compliance reporting và audit logs

Kiến trúc Batch Pipeline điển hình

graph TD
    subgraph Sources
        DB[(Production DB)]
        API[External APIs]
        Files[Log Files / CSV]
    end

    subgraph Orchestration
        AF[Apache Airflow / Dagster]
    end

    subgraph Processing
        SP[Apache Spark]
        DBT[dbt Models]
    end

    subgraph Storage
        DL[(Data Lake - S3/GCS)]
        DW[(Data Warehouse)]
    end

    subgraph Serving
        BI[BI Dashboard]
        ML[ML Training]
    end

    DB --> AF
    API --> AF
    Files --> AF
    AF --> SP
    SP --> DL
    DL --> DBT
    DBT --> DW
    DW --> BI
    DW --> ML

    style AF fill:#e94560,stroke:#fff,color:#fff
    style SP fill:#2c3e50,stroke:#fff,color:#fff
    style DBT fill:#16213e,stroke:#fff,color:#fff
    style DW fill:#e94560,stroke:#fff,color:#fff
    style DL fill:#2c3e50,stroke:#fff,color:#fff

Kiến trúc Batch Pipeline với Airflow + Spark + dbt

Ví dụ: Pipeline tổng hợp doanh thu với dbt

-- models/marts/daily_revenue.sql
WITH orders AS (
    SELECT
        DATE(created_at) AS order_date,
        product_id,
        quantity,
        unit_price,
        quantity * unit_price AS line_total,
        discount_amount
    FROM {{ ref('stg_orders') }}
    WHERE status = 'completed'
),

daily_summary AS (
    SELECT
        order_date,
        COUNT(DISTINCT product_id) AS unique_products,
        SUM(quantity) AS total_items,
        SUM(line_total) AS gross_revenue,
        SUM(discount_amount) AS total_discounts,
        SUM(line_total) - SUM(discount_amount) AS net_revenue
    FROM orders
    GROUP BY order_date
)

SELECT
    order_date,
    unique_products,
    total_items,
    gross_revenue,
    total_discounts,
    net_revenue,
    AVG(net_revenue) OVER (
        ORDER BY order_date
        ROWS BETWEEN 6 PRECEDING AND CURRENT ROW
    ) AS rolling_7d_avg
FROM daily_summary

Orchestrator: Airflow vs Dagster vs Prefect

Apache Airflow vẫn là tiêu chuẩn công nghiệp với cộng đồng lớn nhất. Dagster nổi bật với khái niệm "software-defined assets" — bạn định nghĩa data asset thay vì task, phù hợp hơn cho data-centric workflows. Prefect đơn giản hóa cấu hình với approach "code-first" — không cần DAG definition riêng.

Stream Processing — Xử lý thời gian thực

Stream Processing xử lý dữ liệu liên tục ngay khi nó được tạo ra, với độ trễ từ vài millisecond đến vài giây. Đây là lựa chọn bắt buộc cho các use case đòi hỏi phản hồi tức thì.

Khi nào dùng Streaming?

  • Phát hiện gian lận tài chính (fraud detection) theo thời gian thực
  • Monitoring & alerting hạ tầng
  • Personalization — gợi ý sản phẩm ngay khi user click
  • IoT sensor data processing
  • Real-time dashboards và operational analytics

Kiến trúc Streaming Pipeline

graph LR
    subgraph Producers
        App[Application Events]
        IoT[IoT Devices]
        CDC[Change Data Capture]
    end

    subgraph Message Broker
        K[Apache Kafka / Redpanda]
    end

    subgraph Stream Processor
        F[Apache Flink]
    end

    subgraph Sinks
        RT[(Real-time Store)]
        DL[(Data Lake)]
        Alert[Alerting System]
    end

    App --> K
    IoT --> K
    CDC --> K
    K --> F
    F --> RT
    F --> DL
    F --> Alert

    style K fill:#e94560,stroke:#fff,color:#fff
    style F fill:#2c3e50,stroke:#fff,color:#fff
    style RT fill:#16213e,stroke:#fff,color:#fff

Kiến trúc Streaming Pipeline với Kafka + Flink

// Phát hiện giao dịch bất thường: 3 giao dịch > $1000 trong 5 phút
DataStream<Transaction> transactions = env
    .addSource(new FlinkKafkaConsumer<>(
        "transactions",
        new TransactionDeserializer(),
        kafkaProps
    ));

Pattern<Transaction, ?> fraudPattern = Pattern
    .<Transaction>begin("first")
        .where(new SimpleCondition<Transaction>() {
            public boolean filter(Transaction t) {
                return t.getAmount() > 1000;
            }
        })
    .next("second")
        .where(new SimpleCondition<Transaction>() {
            public boolean filter(Transaction t) {
                return t.getAmount() > 1000;
            }
        })
    .next("third")
        .where(new SimpleCondition<Transaction>() {
            public boolean filter(Transaction t) {
                return t.getAmount() > 1000;
            }
        })
    .within(Time.minutes(5));

PatternStream<Transaction> patternStream = CEP.pattern(
    transactions.keyBy(Transaction::getUserId),
    fraudPattern
);

patternStream.select(
    (Map<String, List<Transaction>> pattern) -> {
        return new FraudAlert(
            pattern.get("first").get(0).getUserId(),
            "3 large transactions in 5 minutes"
        );
    }
).addSink(new AlertSink());

Apache Kafka vs Redpanda vs Amazon Kinesis

Tiêu chí Apache Kafka Redpanda Amazon Kinesis
Kiến trúc JVM-based, cần ZooKeeper/KRaft C++, không cần ZooKeeper Fully managed (AWS)
Latency ~5-10ms (p99) ~1-2ms (p99) ~70-200ms
Throughput Rất cao (millions/sec) Tương đương Kafka Thấp hơn (1MB/s per shard)
Ops complexity Cao (cluster management) Thấp hơn (single binary) Không cần quản lý
Chi phí Tự host hoặc Confluent Cloud Tự host hoặc Redpanda Cloud Pay-per-shard + data
Kafka API compatible ✅ Native ✅ 100% compatible ❌ API riêng

Unified Batch + Streaming: Xu hướng 2026

Một trong những pain point lớn nhất của data engineering là phải duy trì hai hệ thống riêng biệt cho batch và streaming — hai codebase, hai bộ skill, hai CI/CD pipeline. Xu hướng năm 2026 là thống nhất cả hai dưới một framework duy nhất.

graph TD
    subgraph "Shift-Left Architecture"
        S[Data Sources] --> SK[Streaming Layer - Kafka + Flink]
        SK --> |Real-time| RT[Real-time Analytics]
        SK --> |Persist| LH[Lakehouse - Iceberg/Delta]
        LH --> |Batch Transform| DBT[dbt Models]
        DBT --> DW[Serving Layer]
    end

    style SK fill:#e94560,stroke:#fff,color:#fff
    style LH fill:#2c3e50,stroke:#fff,color:#fff
    style DBT fill:#16213e,stroke:#fff,color:#fff
    style DW fill:#e94560,stroke:#fff,color:#fff
    style S fill:#f8f9fa,stroke:#e94560,color:#2c3e50
    style RT fill:#f8f9fa,stroke:#2c3e50,color:#2c3e50

Shift-Left Architecture — streaming là lớp đầu tiên xử lý dữ liệu

Kappa Architecture vs Lambda Architecture

Lambda Architecture (truyền thống)

Duy trì hai path song song: batch layer (tính chính xác) và speed layer (tính real-time). Kết quả được merge tại serving layer. Nhược điểm: phải viết và maintain logic xử lý ở hai nơi — dễ drift và khó debug.

Kappa Architecture (hiện đại)

Chỉ dùng một streaming layer duy nhất cho cả real-time và batch. Batch processing = replay lại event log. Đơn giản hơn nhiều nhưng yêu cầu message broker có khả năng lưu trữ lâu dài (Kafka với infinite retention hoặc tiered storage).

graph TD
    subgraph "Lambda Architecture"
        D1[Data] --> BL[Batch Layer - Spark]
        D1 --> SL[Speed Layer - Flink]
        BL --> SV1[Serving Layer]
        SL --> SV1
    end

    subgraph "Kappa Architecture"
        D2[Data] --> ST[Stream Layer - Flink/Kafka Streams]
        ST --> SV2[Serving Layer]
        ST --> |Replay for reprocessing| ST
    end

    style BL fill:#2c3e50,stroke:#fff,color:#fff
    style SL fill:#e94560,stroke:#fff,color:#fff
    style ST fill:#e94560,stroke:#fff,color:#fff
    style SV1 fill:#16213e,stroke:#fff,color:#fff
    style SV2 fill:#16213e,stroke:#fff,color:#fff

Lambda vs Kappa Architecture

Năm 2026, dbt chính thức hỗ trợ Apache Flink như một adapter — nghĩa là bạn có thể viết dbt model (SQL) để định nghĩa cả batch transformation lẫn streaming transformation. Đây là bước ngoặt quan trọng giúp thống nhất toolchain cho data engineer.

# dbt_project.yml
models:
  my_project:
    staging:
      +materialized: table          # Batch: tạo bảng tĩnh
    real_time:
      +materialized: streaming_table # Streaming: Flink streaming table
    marts:
      +materialized: incremental     # Incremental: append-only
-- models/real_time/rt_user_sessions.sql
-- Materialized as streaming table trên Flink
{{ config(materialized='streaming_table') }}

SELECT
    user_id,
    session_id,
    TUMBLE_START(event_time, INTERVAL '5' MINUTE) AS window_start,
    COUNT(*) AS event_count,
    MAX(event_time) AS last_activity,
    COLLECT(DISTINCT page_url) AS pages_visited
FROM {{ source('kafka', 'user_events') }}
GROUP BY
    user_id,
    session_id,
    TUMBLE(event_time, INTERVAL '5' MINUTE)

Data Quality & Observability

Pipeline tốt nhất cũng vô nghĩa nếu dữ liệu bên trong không đáng tin. Data quality và observability là hai trụ cột không thể thiếu.

Data Quality Checks với dbt Tests

# models/staging/schema.yml
version: 2

models:
  - name: stg_orders
    columns:
      - name: order_id
        tests:
          - unique
          - not_null
      - name: amount
        tests:
          - not_null
          - dbt_expectations.expect_column_values_to_be_between:
              min_value: 0
              max_value: 1000000
      - name: status
        tests:
          - accepted_values:
              values: ['pending', 'completed', 'cancelled', 'refunded']
      - name: created_at
        tests:
          - not_null
          - dbt_expectations.expect_column_values_to_be_recent:
              datepart: day
              interval: 3

Data Contract — Giao kèo giữa Producer và Consumer

Data Contract là gì?

Data Contract là một thỏa thuận formal giữa team sản xuất dữ liệu (producer) và team tiêu thụ dữ liệu (consumer) về schema, chất lượng, SLA và ownership của dataset. Nó giúp ngăn chặn "breaking changes" âm thầm — khi team backend đổi tên cột mà không báo team analytics.

# contracts/orders_v2.yaml
apiVersion: v2
kind: DataContract
metadata:
  name: orders
  owner: backend-team
  domain: e-commerce

schema:
  type: object
  properties:
    order_id:
      type: string
      format: uuid
      required: true
    user_id:
      type: string
      required: true
    amount:
      type: number
      minimum: 0
    currency:
      type: string
      enum: [VND, USD, EUR]

quality:
  - type: freshness
    threshold: "PT1H"    # Dữ liệu không được cũ hơn 1 giờ
  - type: completeness
    column: amount
    threshold: 0.99       # 99% rows phải có giá trị

sla:
  availability: 99.9%
  latency: "PT5M"         # Max 5 phút từ source đến warehouse

Modern Data Stack 2026

Dưới đây là bộ công cụ được sử dụng rộng rãi nhất trong kiến trúc data pipeline hiện đại:

graph TD
    subgraph "Ingestion"
        FV[Fivetran / Airbyte]
        DB[Debezium CDC]
    end

    subgraph "Storage"
        S3[Object Storage - S3/GCS/R2]
        ICE[Apache Iceberg / Delta Lake]
    end

    subgraph "Processing"
        SPK[Apache Spark]
        FLK[Apache Flink]
        DBTL[dbt Core / dbt Cloud]
    end

    subgraph "Orchestration"
        DAG[Dagster / Airflow / Prefect]
    end

    subgraph "Serving"
        SNF[Snowflake / BigQuery / Databricks]
        RS[Redis / Druid - Low Latency]
    end

    subgraph "Observability"
        MC[Monte Carlo / Elementary]
        GE[Great Expectations]
    end

    FV --> S3
    DB --> S3
    S3 --> ICE
    ICE --> SPK
    ICE --> FLK
    SPK --> DBTL
    FLK --> DBTL
    DAG --> SPK
    DAG --> FLK
    DAG --> DBTL
    DBTL --> SNF
    DBTL --> RS
    MC --> DBTL
    GE --> DBTL

    style FV fill:#e94560,stroke:#fff,color:#fff
    style DBTL fill:#e94560,stroke:#fff,color:#fff
    style SNF fill:#2c3e50,stroke:#fff,color:#fff
    style ICE fill:#16213e,stroke:#fff,color:#fff
    style DAG fill:#2c3e50,stroke:#fff,color:#fff

Modern Data Stack 2026 — từ ingestion đến serving

Chi phí thực tế

Thành phần Giải pháp miễn phí / giá rẻ Giải pháp enterprise
Ingestion Airbyte OSS (self-host free) Fivetran ($1/credit)
Storage MinIO + Iceberg (self-host) Snowflake / Databricks
Transform dbt Core (free, CLI) dbt Cloud ($100/seat/mo)
Orchestration Dagster OSS / Airflow Dagster Cloud / Astronomer
Message Broker Redpanda Community (free) Confluent Cloud
Observability Elementary (dbt native, free) Monte Carlo

⚠️ Cạm bẫy thường gặp

Over-engineering: Không phải mọi team đều cần Kafka + Flink + Spark. Nếu dữ liệu của bạn dưới 100GB/ngày và latency yêu cầu > 1 giờ, một batch pipeline đơn giản với Airbyte + dbt + PostgreSQL/BigQuery là đủ. Streaming pipeline chỉ thêm giá trị khi bạn thực sự cần xử lý real-time.

Thiết kế Pipeline: Decision Framework

Khi thiết kế data pipeline, hãy trả lời 4 câu hỏi sau để chọn kiến trúc phù hợp:

graph TD
    Q1{Dữ liệu cần xử lý
trong bao lâu?} Q1 -->|Dưới 1 phút| STREAM[Streaming Pipeline] Q1 -->|1 phút - 1 giờ| MICRO[Micro-Batch] Q1 -->|Hơn 1 giờ| BATCH[Batch Pipeline] STREAM --> Q2{Volume?} Q2 -->|>100K events/sec| KAFKA[Kafka + Flink] Q2 -->|<100K events/sec| MANAGED[Kinesis / Pub-Sub] BATCH --> Q3{Data size/ngày?} Q3 -->|>1TB| SPARK[Spark + Iceberg] Q3 -->|<1TB| SIMPLE[dbt + DW] MICRO --> Q4{Complexity?} Q4 -->|Đơn giản| SSTREAM[Spark Structured Streaming] Q4 -->|Phức tạp CEP| FLINK2[Apache Flink] style Q1 fill:#e94560,stroke:#fff,color:#fff style STREAM fill:#2c3e50,stroke:#fff,color:#fff style BATCH fill:#2c3e50,stroke:#fff,color:#fff style MICRO fill:#2c3e50,stroke:#fff,color:#fff style KAFKA fill:#f8f9fa,stroke:#e94560,color:#2c3e50 style MANAGED fill:#f8f9fa,stroke:#e94560,color:#2c3e50 style SPARK fill:#f8f9fa,stroke:#e94560,color:#2c3e50 style SIMPLE fill:#f8f9fa,stroke:#e94560,color:#2c3e50 style SSTREAM fill:#f8f9fa,stroke:#e94560,color:#2c3e50 style FLINK2 fill:#f8f9fa,stroke:#e94560,color:#2c3e50

Decision tree chọn kiến trúc Data Pipeline

Best Practices cho Production Pipeline

1. Idempotency — Chạy lại không sợ trùng

Mọi pipeline phải idempotent — chạy lại cùng input cho cùng output. Dùng MERGE/UPSERT thay vì INSERT, và partition dữ liệu theo ngày để có thể reprocess từng partition độc lập.

-- Idempotent load pattern
MERGE INTO fact_orders AS target
USING staging_orders AS source
ON target.order_id = source.order_id
WHEN MATCHED THEN UPDATE SET
    amount = source.amount,
    status = source.status,
    updated_at = CURRENT_TIMESTAMP
WHEN NOT MATCHED THEN INSERT (order_id, amount, status, created_at)
VALUES (source.order_id, source.amount, source.status, source.created_at);

2. Schema Evolution — Thay đổi schema không gãy pipeline

Sử dụng Apache Iceberg hoặc Delta Lake để hỗ trợ schema evolution tự động — thêm cột mới không cần rewrite toàn bộ dữ liệu. Kết hợp Data Contract để đảm bảo backward compatibility.

3. Backfill Strategy — Bổ sung dữ liệu lịch sử

Luôn thiết kế pipeline có khả năng backfill — xử lý lại dữ liệu từ một thời điểm trong quá khứ. Partition theo ngày/tháng và parameterize date range trong DAG configuration.

4. Monitoring & Alerting

  • Pipeline health: Thời gian chạy, success/failure rate, data freshness
  • Data quality: Row count anomalies, null rate, schema drift
  • Cost tracking: Compute hours, storage growth, query cost

Kết luận

Kiến trúc Data Pipeline năm 2026 đang hội tụ về ba xu hướng chính: ELT thay thế ETL nhờ sức mạnh compute của cloud warehouse, Unified Batch + Streaming với dbt + Flink xóa nhòa ranh giới giữa hai thế giới, và Shift-Left Architecture đưa transformation, quality checks và governance vào streaming layer ngay từ đầu. Chìa khóa thành công không phải là chọn công nghệ "hot" nhất, mà là hiểu rõ yêu cầu về latency, volume và complexity của bài toán cụ thể — rồi chọn giải pháp đơn giản nhất đáp ứng được yêu cầu đó.

💡 Lời khuyên

Bắt đầu với batch pipeline đơn giản (Airbyte + dbt + BigQuery/PostgreSQL). Chỉ thêm streaming khi bạn có use case thực sự cần real-time. Premature complexity là kẻ thù lớn nhất của data engineering.

Tham khảo