Delta Lake vs Apache Iceberg 2026: Kiến trúc Lakehouse và Câu hỏi Phỏng vấn

Hướng dẫn chi tiết so sánh Delta Lake và Apache Iceberg cho kiến trúc lakehouse. Bao gồm ví dụ code, best practices và câu hỏi phỏng vấn data engineering 2026.

Delta Lake vs Apache Iceberg 2026: Kiến trúc Lakehouse và Câu hỏi Phỏng vấn

Kiến trúc data lakehouse đã trở thành tiêu chuẩn công nghiệp cho các nền tảng dữ liệu hiện đại vào năm 2026. Hai định dạng bảng mở chiếm ưu thế trong lĩnh vực này là Delta Lake và Apache Iceberg. Cả hai công nghệ này cho phép các tổ chức kết hợp ưu điểm của data lake (lưu trữ giá rẻ, định dạng mở, khả năng mở rộng) với khả năng của data warehouse (giao dịch ACID, schema evolution, time travel). Bài viết này phân tích chuyên sâu về sự khác biệt giữa Delta Lake và Apache Iceberg, triển khai thực tế, cũng như các câu hỏi phỏng vấn thường gặp trong lĩnh vực data engineering.

Định dạng bảng Lakehouse là gì?

Định dạng bảng lakehouse là một lớp metadata nằm trên các file dữ liệu (Parquet, ORC) trong object storage như S3, GCS, hoặc Azure Blob. Định dạng này cung cấp các tính năng như giao dịch ACID, schema enforcement và versioning mà không cần database truyền thống. Delta Lake được phát triển bởi Databricks, trong khi Apache Iceberg được tạo ra bởi Netflix và hiện là dự án top-level của Apache. Cả hai đều hỗ trợ multiple compute engines và là nền tảng của kiến trúc lakehouse hiện đại.

Kiến trúc Delta Lake: Cấu trúc và Thành phần Chính

Delta Lake sử dụng transaction log dựa trên JSON có tên _delta_log để theo dõi tất cả các thay đổi trên bảng. Mỗi thao tác ghi tạo ra một file JSON mới ghi lại các file được thêm hoặc xóa. Cách tiếp cận này cho phép ACID transactions và concurrent reads/writes an toàn.

Cấu trúc thư mục Delta Lake như sau:

text
my_delta_table/
├── _delta_log/
│   ├── 00000000000000000000.json
│   ├── 00000000000000000001.json
│   ├── 00000000000000000002.json
│   └── 00000000000000000010.checkpoint.parquet
├── part-00000-xxx.parquet
├── part-00001-xxx.parquet
└── part-00002-xxx.parquet

Tạo và ghi vào Delta table sử dụng PySpark:

python
from delta import DeltaTable
from pyspark.sql import SparkSession

spark = SparkSession.builder \
    .appName("DeltaLakeDemo") \
    .config("spark.sql.extensions", "io.delta.sql.DeltaSparkSessionExtension") \
    .config("spark.sql.catalog.spark_catalog", "org.apache.spark.sql.delta.catalog.DeltaCatalog") \
    .getOrCreate()

# Create Delta table with schema
df = spark.createDataFrame([
    (1, "Alice", "2026-01-15", 50000.00),
    (2, "Bob", "2026-01-16", 75000.00),
    (3, "Charlie", "2026-01-17", 60000.00)
], ["customer_id", "name", "signup_date", "revenue"])

df.write.format("delta").mode("overwrite").save("/data/customers_delta")

Delta Lake cung cấp tính năng MERGE mạnh mẽ cho các thao tác upsert:

python
from delta.tables import DeltaTable

# Load existing Delta table
delta_table = DeltaTable.forPath(spark, "/data/customers_delta")

# New data to merge
updates_df = spark.createDataFrame([
    (2, "Bob Smith", "2026-01-16", 80000.00),  # Update existing
    (4, "Diana", "2026-01-18", 45000.00)        # Insert new
], ["customer_id", "name", "signup_date", "revenue"])

# Perform MERGE operation
delta_table.alias("target").merge(
    updates_df.alias("source"),
    "target.customer_id = source.customer_id"
).whenMatchedUpdate(set={
    "name": "source.name",
    "revenue": "source.revenue"
}).whenNotMatchedInsert(values={
    "customer_id": "source.customer_id",
    "name": "source.name",
    "signup_date": "source.signup_date",
    "revenue": "source.revenue"
}).execute()

Apache Iceberg: Kiến trúc và Ưu điểm

Apache Iceberg sử dụng cách tiếp cận khác với việc lưu trữ metadata theo cấu trúc phân cấp ba tầng: metadata files, manifest lists, và manifest files. Kiến trúc này cho phép snapshot isolation hiệu quả hơn và partition evolution mà không cần rewrite dữ liệu.

Cấu trúc metadata của Iceberg:

text
warehouse/
└── my_iceberg_table/
    ├── metadata/
    │   ├── v1.metadata.json
    │   ├── v2.metadata.json
    │   ├── snap-xxx-1.avro (manifest list)
    │   └── xxx-m0.avro (manifest file)
    └── data/
        ├── partition=2026-01/
        │   ├── 00000-0-xxx.parquet
        │   └── 00001-0-xxx.parquet
        └── partition=2026-02/
            └── 00000-0-xxx.parquet

Tạo và thao tác với Iceberg table:

python
from pyspark.sql import SparkSession

spark = SparkSession.builder \
    .appName("IcebergDemo") \
    .config("spark.sql.extensions", "org.apache.iceberg.spark.extensions.IcebergSparkSessionExtensions") \
    .config("spark.sql.catalog.local", "org.apache.iceberg.spark.SparkCatalog") \
    .config("spark.sql.catalog.local.type", "hadoop") \
    .config("spark.sql.catalog.local.warehouse", "/warehouse") \
    .getOrCreate()

# Create Iceberg table with partitioning
spark.sql("""
    CREATE TABLE local.db.events (
        event_id BIGINT,
        event_type STRING,
        user_id BIGINT,
        event_timestamp TIMESTAMP,
        properties MAP<STRING, STRING>
    )
    USING iceberg
    PARTITIONED BY (days(event_timestamp))
""")

# Insert data
spark.sql("""
    INSERT INTO local.db.events VALUES
    (1, 'page_view', 100, timestamp '2026-01-15 10:30:00', map('page', '/home')),
    (2, 'click', 100, timestamp '2026-01-15 10:31:00', map('button', 'signup')),
    (3, 'purchase', 101, timestamp '2026-01-15 11:00:00', map('amount', '99.99'))
""")

Một trong những ưu điểm chính của Iceberg là hidden partitioning, tách biệt partition scheme khỏi query:

python
# Partition evolution - change partitioning without rewriting data
spark.sql("""
    ALTER TABLE local.db.events
    ADD PARTITION FIELD hours(event_timestamp)
""")

# Query automatically uses optimal partition pruning
# Users don't need to know the partition structure
result = spark.sql("""
    SELECT event_type, count(*) as event_count
    FROM local.db.events
    WHERE event_timestamp >= '2026-01-15'
      AND event_timestamp < '2026-01-16'
    GROUP BY event_type
""")

So sánh Tính năng: Delta Lake vs Apache Iceberg

Dưới đây là bảng so sánh toàn diện giữa Delta Lake và Apache Iceberg dựa trên các tính năng chính:

| Tính năng | Delta Lake | Apache Iceberg | |-----------|------------|----------------| | Transaction Log | JSON files với checkpoint | Metadata hierarchy multi-level | | Schema Evolution | Add/rename columns | Full evolution bao gồm reorder | | Partition Evolution | Cần rewrite | In-place không cần rewrite | | Time Travel | Dựa trên version/timestamp | Snapshot-based với retention | | Compute Engine Support | Tối ưu cho Spark/Databricks | Multi-engine (Spark, Flink, Trino, Presto) | | Catalog Integration | Unity Catalog, Hive | REST, Hive, AWS Glue, Nessie | | Delete Operations | Copy-on-write, merge-on-read | Copy-on-write, merge-on-read | | Community | Databricks dẫn đầu | Apache Foundation, multi-vendor |

Time travel cho audit và debugging:

python
# Delta Lake time travel
df_v1 = spark.read.format("delta").option("versionAsOf", 1).load("/data/customers_delta")
df_timestamp = spark.read.format("delta").option("timestampAsOf", "2026-01-15 10:00:00").load("/data/customers_delta")

# Iceberg time travel
spark.sql("SELECT * FROM local.db.events VERSION AS OF 1")
spark.sql("SELECT * FROM local.db.events TIMESTAMP AS OF '2026-01-15 10:00:00'")

# Iceberg snapshot history
spark.sql("SELECT * FROM local.db.events.history").show()

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.

Tối ưu Hiệu suất và Bảo trì

Cả hai định dạng đều cần bảo trì định kỳ để đạt hiệu suất tối ưu. Delta Lake sử dụng OPTIMIZE và VACUUM, trong khi Iceberg sử dụng compaction và expire snapshots.

Tối ưu hóa Delta Lake:

python
from delta.tables import DeltaTable

delta_table = DeltaTable.forPath(spark, "/data/customers_delta")

# Compact small files into larger ones
delta_table.optimize().executeCompaction()

# Z-order for query optimization on specific columns
delta_table.optimize().executeZOrderBy("signup_date", "customer_id")

# Remove old versions (retain 7 days)
spark.sql("VACUUM '/data/customers_delta' RETAIN 168 HOURS")

# View table history
delta_table.history().show()

Tối ưu hóa Apache Iceberg:

python
# Compact data files
spark.sql("""
    CALL local.system.rewrite_data_files(
        table => 'db.events',
        strategy => 'binpack',
        options => map('target-file-size-bytes', '134217728')
    )
""")

# Sort data for better query performance
spark.sql("""
    CALL local.system.rewrite_data_files(
        table => 'db.events',
        strategy => 'sort',
        sort_order => 'event_timestamp ASC, event_type ASC'
    )
""")

# Expire old snapshots
spark.sql("""
    CALL local.system.expire_snapshots(
        table => 'db.events',
        older_than => TIMESTAMP '2026-01-08 00:00:00',
        retain_last => 5
    )
""")

# Remove orphan files
spark.sql("""
    CALL local.system.remove_orphan_files(
        table => 'db.events',
        older_than => TIMESTAMP '2026-01-01 00:00:00'
    )
""")

Tích hợp với Data Pipeline Hiện đại

Cả hai định dạng đều tích hợp tốt trong hệ sinh thái dữ liệu hiện đại. Dưới đây là ví dụ pipeline với Apache Airflow:

python
from airflow import DAG
from airflow.providers.apache.spark.operators.spark_submit import SparkSubmitOperator
from datetime import datetime, timedelta

default_args = {
    'owner': 'data-engineering',
    'depends_on_past': False,
    'email_on_failure': True,
    'retries': 2,
    'retry_delay': timedelta(minutes=5)
}

with DAG(
    'lakehouse_etl_pipeline',
    default_args=default_args,
    schedule_interval='@hourly',
    start_date=datetime(2026, 1, 1),
    catchup=False
) as dag:

    ingest_to_bronze = SparkSubmitOperator(
        task_id='ingest_raw_events',
        application='/jobs/ingest_events.py',
        conf={
            'spark.sql.extensions': 'io.delta.sql.DeltaSparkSessionExtension',
            'spark.jars.packages': 'io.delta:delta-spark_2.12:3.1.0'
        }
    )

    transform_to_silver = SparkSubmitOperator(
        task_id='transform_events',
        application='/jobs/transform_silver.py'
    )

    aggregate_to_gold = SparkSubmitOperator(
        task_id='aggregate_metrics',
        application='/jobs/aggregate_gold.py'
    )

    optimize_tables = SparkSubmitOperator(
        task_id='optimize_delta_tables',
        application='/jobs/optimize_maintenance.py'
    )

    ingest_to_bronze >> transform_to_silver >> aggregate_to_gold >> optimize_tables

Câu hỏi Phỏng vấn Data Engineering: Kiến trúc Lakehouse

Dưới đây là các câu hỏi phỏng vấn thường gặp liên quan đến Delta Lake và Apache Iceberg vào năm 2026:

Câu 1: Giải thích sự khác biệt chính giữa transaction log của Delta Lake và metadata architecture của Apache Iceberg. Cái nào phù hợp hơn cho trường hợp sử dụng cụ thể?

Delta Lake sử dụng transaction log dựa trên JSON theo kiểu sequential, trong đó mỗi thao tác tạo ra một file JSON mới. Checkpoint files ở định dạng Parquet được tạo định kỳ để tăng tốc việc đọc state mới nhất. Cách tiếp cận này đơn giản và hiệu quả, nhưng cần đọc nhiều file JSON để tái tạo state trên các cluster lớn.

Apache Iceberg sử dụng metadata hierarchy ba tầng: metadata files (JSON) trỏ đến manifest lists (Avro), sau đó trỏ đến manifest files (Avro) chứa thông tin file dữ liệu. Kiến trúc này cho phép pruning hiệu quả hơn vì statistics được lưu ở cấp manifest, và snapshot isolation tốt hơn cho concurrent queries.

Delta Lake phù hợp hơn cho các team đã sử dụng Databricks ecosystem, cần tích hợp chặt chẽ với Spark, và muốn setup đơn giản hơn. Iceberg vượt trội trong môi trường multi-engine (Spark, Flink, Trino), cần partition evolution mà không cần data rewrite, hoặc làm việc với catalogs như AWS Glue hoặc Apache Nessie.

Câu 2: Partition evolution hoạt động như thế nào trong Apache Iceberg? Tại sao đây là một ưu điểm đáng kể so với cách tiếp cận truyền thống?

Partition evolution trong Iceberg cho phép thay đổi partition scheme mà không cần viết lại dữ liệu hiện có. Khi partition scheme thay đổi, Iceberg lưu thông tin này trong metadata và tự động xử lý các query bao gồm dữ liệu với cả partition cũ và mới.

Ví dụ, nếu bảng ban đầu được phân vùng theo tháng (month(event_date)) và sau đó chuyển sang ngày (day(event_date)), dữ liệu cũ vẫn ở trong partition theo tháng trong khi dữ liệu mới được ghi vào partition theo ngày. Query planner của Iceberg tự động áp dụng partition pruning phù hợp cho cả hai loại partition.

Điều này khác biệt đáng kể so với cách tiếp cận Hive truyền thống hoặc thậm chí Delta Lake, nơi thay đổi partition yêu cầu rewrite toàn bộ bảng. Với các bảng petabyte-scale, điều này tiết kiệm đáng kể thời gian và chi phí compute.

Câu 3: Copy-on-write vs merge-on-read là gì? Khi nào nên sử dụng mỗi chiến lược?

Copy-on-write (COW) viết lại toàn bộ file dữ liệu khi có update hoặc delete. Cách tiếp cận này cho read performance tối ưu vì không có overhead khi query, nhưng write performance có thể chậm với các updates thường xuyên.

Merge-on-read (MOR) lưu deletes và updates trong các file riêng biệt (delete files) và merge chúng tại thời điểm đọc. Write performance nhanh hơn, nhưng reads cần overhead cho việc merging.

Sử dụng copy-on-write cho: workloads có nhiều reads hơn writes, batch processing với update window rõ ràng, và các trường hợp read latency quan trọng.

Sử dụng merge-on-read cho: streaming ingestion với frequent updates, workloads có high write throughput, và các trường hợp write latency quan trọng hơn read latency. Trong thực tế, nhiều team kết hợp cả hai với compaction job định kỳ chuyển đổi MOR files sang COW.

Câu 4: Làm thế nào để triển khai data quality checks trong kiến trúc lakehouse?

Data quality trong kiến trúc lakehouse được triển khai ở nhiều layer:

Ở ingestion layer, sử dụng schema enforcement được cung cấp bởi Delta Lake hoặc Iceberg để từ chối dữ liệu không phù hợp schema. Delta Lake cung cấp các constraint như CHECK constraints và NOT NULL.

Ở transformation layer, các công cụ như Great Expectations hoặc dbt tests có thể được tích hợp để validate dữ liệu. Ví dụ, validate rằng không có giá trị null ở các cột quan trọng, giá trị nằm trong range mong đợi, hoặc referential integrity được bảo toàn.

Ở serving layer, triển khai monitoring cho data freshness và completeness. Alert khi bảng không được update trong window mong đợi hoặc khi row counts thay đổi bất thường.

Câu 5: Giải thích chiến lược migration từ Hive tables sang định dạng lakehouse (Delta/Iceberg).

Migration từ Hive sang định dạng lakehouse có thể thực hiện theo nhiều cách:

In-place conversion cho Iceberg: sử dụng procedure migrate thêm metadata Iceberg vào các Parquet files hiện có mà không cần di chuyển dữ liệu. Điều này cho phép migration nhanh nhưng không có lợi ích của file optimization.

CTAS (Create Table As Select) migration: tạo bảng mới ở định dạng đích và copy dữ liệu. Cách tiếp cận này cho phép optimization như re-partitioning và file sizing, nhưng cần storage bổ sung và downtime.

Dual-write migration: ghi vào cả hai định dạng trong giai đoạn chuyển đổi, validate consistency, sau đó cutover sang định dạng mới. Điều này giảm thiểu rủi ro nhưng cần nhiều công sức engineering hơn.

Delta Lake cũng cung cấp CONVERT TO DELTA để chuyển đổi Parquet tables hiện có. Best practice là bắt đầu với các bảng ít quan trọng, validate kỹ lưỡng, sau đó migrate các bảng quan trọng từng bước.

Best Practices cho Production Lakehouse

Quản lý lakehouse trong production đòi hỏi chú ý đến một số khía cạnh quan trọng:

python
# Example: Production-ready table configuration for Delta Lake
spark.sql("""
    CREATE TABLE IF NOT EXISTS prod.analytics.user_events (
        event_id STRING NOT NULL,
        user_id BIGINT NOT NULL,
        event_type STRING NOT NULL,
        event_properties MAP<STRING, STRING>,
        event_timestamp TIMESTAMP NOT NULL,
        processing_timestamp TIMESTAMP NOT NULL,
        _partition_date DATE GENERATED ALWAYS AS (CAST(event_timestamp AS DATE))
    )
    USING DELTA
    PARTITIONED BY (_partition_date)
    TBLPROPERTIES (
        'delta.autoOptimize.optimizeWrite' = 'true',
        'delta.autoOptimize.autoCompact' = 'true',
        'delta.logRetentionDuration' = 'interval 30 days',
        'delta.deletedFileRetentionDuration' = 'interval 7 days',
        'delta.columnMapping.mode' = 'name'
    )
""")

# Example: Production-ready table configuration for Iceberg
spark.sql("""
    CREATE TABLE IF NOT EXISTS local.analytics.user_events (
        event_id STRING NOT NULL,
        user_id BIGINT NOT NULL,
        event_type STRING NOT NULL,
        event_properties MAP<STRING, STRING>,
        event_timestamp TIMESTAMP NOT NULL,
        processing_timestamp TIMESTAMP NOT NULL
    )
    USING ICEBERG
    PARTITIONED BY (days(event_timestamp))
    TBLPROPERTIES (
        'write.format.default' = 'parquet',
        'write.parquet.compression-codec' = 'zstd',
        'write.target-file-size-bytes' = '134217728',
        'history.expire.max-snapshot-age-ms' = '604800000',
        'write.metadata.delete-after-commit.enabled' = 'true'
    )
""")

Monitoring và observability rất quan trọng cho lakehouse production. Triển khai metrics cho: số lượng file mỗi partition (để phát hiện vấn đề small files), latency của thao tác write và read, storage utilization và tốc độ tăng trưởng, cũng như query performance patterns.

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: Chọn Định dạng Phù hợp

Delta Lake và Apache Iceberg đều là các giải pháp mature cho kiến trúc lakehouse vào năm 2026. Việc lựa chọn giữa hai công nghệ này phụ thuộc vào một số yếu tố:

Chọn Delta Lake nếu: tổ chức đã sử dụng Databricks làm nền tảng chính, team cần tích hợp chặt chẽ với Spark ecosystem, hoặc cần các tính năng như Change Data Feed cho streaming CDC.

Chọn Apache Iceberg nếu: cần khả năng tương tác multi-engine (Spark, Flink, Trino, Presto), partition evolution mà không cần data rewrite là yêu cầu quan trọng, hoặc ưu tiên các tiêu chuẩn mở với governance của Apache Foundation.

Trong thực tế, nhiều tổ chức áp dụng cả hai định dạng cho các use cases khác nhau. Điều quan trọng nhất là hiểu trade-offs của từng công nghệ và lựa chọn dựa trên nhu cầu cụ thể của workload.

Các điểm chính cần nhớ:

  • Transaction log vs metadata hierarchy: Delta Lake sử dụng JSON logs, Iceberg sử dụng multi-level metadata
  • Partition evolution: Iceberg vượt trội với in-place evolution mà không cần data rewrite
  • Engine support: Delta Lake tối ưu cho Spark, Iceberg hỗ trợ multi-engine
  • Maintenance: Cả hai định dạng đều cần compaction và cleanup định kỳ
  • Interview preparation: Hiểu kiến trúc internal, không chỉ syntax API

Thành thạo cả hai công nghệ này mang lại sự linh hoạt trong việc đối mặt với các thách thức data engineering khác nhau và chuẩn bị cho các ứng viên những câu hỏi phỏng vấn ngày càng chuyên sâu về kiến trúc lakehouse hiện đại.

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.

Chia sẻ

Bài viết liên quan