Apache Iceberg: Tấm sổ đỏ biến Data Lake thành Lakehouse


Một nhà kho chứa một tỷ thùng hàng

Giả sử công ty có một nhà kho rất lớn.

Mỗi ngày, hàng triệu thùng hàng được đưa vào.

Thùng màu xanh chứa đơn bán hàng.

Thùng màu đỏ chứa tồn kho.

Thùng màu vàng chứa thông tin khách hàng.

Thùng màu tím chứa log từ website.

Ban đầu, số lượng hàng còn ít. Nhân viên chỉ cần ghi bên ngoài:

1/kho/don-hang/2026/07/25/

Muốn tìm đơn hàng ngày 25 tháng 7 thì đi vào đúng căn phòng đó.

Xong thuật toán, quá dễ.

Nhưng vài năm sau, nhà kho có hàng tỷ thùng.

Có thùng được đưa vào trễ.

Có thùng ghi nhầm ngày.

Có thùng cần sửa.

Có thùng cần xóa vì khách hàng yêu cầu.

Có hai đội cùng lúc mang hàng vào một khu vực.

Có một đội đang đọc số liệu thì đội khác thay toàn bộ hàng trên kệ.

Lúc này, vấn đề không còn là thiếu chỗ chứa.

Vấn đề là không ai biết chính xác:

  • Thùng nào đang thuộc bảng nào.
  • Phiên bản hiện tại gồm những thùng nào.
  • Thùng nào đã bị thay thế.
  • Thùng nào chỉ mới được ghi một nửa.
  • Cấu trúc dữ liệu đã thay đổi từ lúc nào.
  • Một câu truy vấn cần mở những thùng nào.
  • Làm sao quay lại trạng thái của ngày hôm qua.

Đây chính là câu chuyện của Data Lake thế hệ đầu.

Chúng ta xây được một cái hồ rất lớn.

Nhưng hồ chỉ biết chứa nước.

Nó không biết đâu là chai nước đang được bán, đâu là chai đã hết hạn, đâu là chai vừa bị một người khác đổ thêm nước vào.

Apache Iceberg xuất hiện để giải quyết phần còn thiếu đó.

Nó không thay cái hồ.

Nó đặt lên trên cái hồ một hệ thống quản lý bảng đủ nghiêm túc để các công cụ xử lý dữ liệu có thể cùng đọc, cùng ghi mà không phải nhìn vào hàng triệu file rồi cầu nguyện.

Data Lake 1.0 là gì?

“Data Lake 1.0” không phải tên phiên bản chính thức của một sản phẩm.

Trong bài viết này, mình dùng nó để chỉ thế hệ Data Lake đầu tiên, nơi dữ liệu được đưa từ nhiều hệ thống về một vùng lưu trữ tập trung, thường là HDFS hoặc object storage, sau đó được xử lý bằng những công cụ như Hive, Spark và Presto.

Kiến trúc cơ bản có dạng:

 1ERP / CRM / Website / App / Log
 2 3        Công cụ thu thập dữ liệu
 4 5          HDFS hoặc Object Storage
 6 7       Parquet / ORC / Avro / JSON
 8 9          Hive Metastore
1011       Spark / Hive / Presto / Trino
1213            Báo cáo và BI

HDFS là một hệ thống file phân tán, được thiết kế để lưu dữ liệu lớn trên nhiều máy phổ thông. Nó chia file thành các block, phân phối chúng qua nhiều DataNode và dùng NameNode để quản lý không gian tên của hệ thống file.

Khi điện toán đám mây trở nên phổ biến, S3, Azure Data Lake Storage và Google Cloud Storage dần đảm nhiệm vai trò cái hồ. Thay vì ổ đĩa nằm trong cụm Hadoop, dữ liệu nằm trên object storage và các hệ thống xử lý thuê tài nguyên khi cần.

Đây là một ý tưởng rất mạnh:

Tách nơi chứa dữ liệu khỏi nơi xử lý dữ liệu.

Dữ liệu có thể nằm yên trong kho.

Spark đến xử lý rồi rời đi.

Trino đến đọc rồi rời đi.

Flink đến ghi dữ liệu rồi tiếp tục chạy.

Không cần mỗi hệ thống phải giữ một bản sao riêng.

Nhưng tách storage khỏi compute cũng tạo ra một khoảng trống lớn.

Object storage chỉ nhìn thấy:

  • Bucket.
  • Folder.
  • Object.
  • Tên file.
  • Kích thước file.
  • Thời gian cập nhật.

Nó không thực sự hiểu:

  • Bảng.
  • Giao dịch.
  • Khóa chính.
  • Phiên bản.
  • Snapshot.
  • Schema.
  • Partition.
  • File nào đã bị loại khỏi bảng.

Nó giống một bãi container ở cảng.

Container được xếp rất ngay ngắn.

Nhưng nếu không có hệ thống vận đơn, con người chỉ đang đứng trước hàng triệu chiếc hộp kim loại giống nhau.

Data Lake đời đầu được tạo từ những lớp nào?

Một Data Lake không phải một phần mềm duy nhất.

Nó là nhiều lớp được ghép lại.

Lớp lưu trữ: HDFS, S3, ADLS và GCS

Đây là nền đất.

Nó chịu trách nhiệm giữ các file dữ liệu.

Trong hệ thống tự vận hành tại trung tâm dữ liệu, HDFS từng là lựa chọn phổ biến. Trong môi trường cloud, object storage thường được chọn vì dung lượng lớn, độ bền cao và khả năng tách biệt khỏi cụm tính toán.

Storage không nên chịu trách nhiệm hiểu toàn bộ logic của bảng.

Nó chỉ cần giữ file một cách đáng tin cậy.

Lớp định dạng file: Parquet, ORC và Avro

Nếu object storage là nhà kho, file format là cách hàng hóa được đóng thùng.

Parquet

Parquet là định dạng dạng cột, được thiết kế cho việc lưu trữ và đọc dữ liệu phân tích hiệu quả. Các giá trị của cùng một cột được đặt gần nhau, giúp hệ thống chỉ đọc những cột thật sự cần và nén dữ liệu tốt hơn.

Ví dụ bảng bán hàng có 100 cột, nhưng báo cáo chỉ cần:

  • Ngày bán.
  • Mã cửa hàng.
  • Doanh thu.

Với CSV, hệ thống thường phải kéo qua cả dòng dữ liệu.

Với Parquet, nó có thể tập trung vào ba cột cần thiết.

Parquet trả lời câu hỏi:

Dữ liệu bên trong một file được sắp xếp như thế nào?

Nó không trả lời:

Những file Parquet nào đang thuộc phiên bản hiện tại của bảng?

Đây là một điểm cực kỳ quan trọng.

Parquet là file format. Iceberg là table format.

Một cái quản lý nội thất bên trong từng căn nhà.

Một cái quản lý cả khu dân cư, sổ nhà đất, địa chỉ và lịch sử thay đổi.

ORC

ORC cũng là một định dạng dạng cột, được sử dụng nhiều trong hệ sinh thái Hive.

Nó cùng nhóm bài toán với Parquet: lưu dữ liệu phân tích sao cho đọc nhanh, nén tốt và hỗ trợ bỏ qua những vùng dữ liệu không cần thiết.

Avro

Avro thường phù hợp với dữ liệu dạng dòng hoặc dữ liệu sự kiện, nơi schema đi cùng dữ liệu và việc tuần tự hóa từng record quan trọng hơn truy vấn phân tích dạng cột.

Trong một pipeline thực tế, Kafka có thể nhận sự kiện dạng Avro hoặc JSON, sau đó Spark hoặc Flink chuyển chúng thành Parquet để phục vụ phân tích.

Lớp catalog: Hive Metastore

File đã có.

Nhưng hệ thống vẫn cần biết:

  • Bảng tên gì.
  • Có những cột nào.
  • Dữ liệu nằm ở đường dẫn nào.
  • Bảng được chia partition ra sao.
  • Dùng file format nào.

Hive Metastore được dùng để giữ các metadata này. Tài liệu Hive mô tả metadata của bảng và partition được truy cập thông qua Metastore, còn thông tin đó thường được lưu trong một cơ sở dữ liệu quan hệ phía sau.

Ví dụ:

 1CREATE EXTERNAL TABLE sales (
 2    order_id BIGINT,
 3    store_id INT,
 4    revenue DECIMAL(18, 2)
 5)
 6PARTITIONED BY (
 7    sale_date STRING
 8)
 9STORED AS PARQUET
10LOCATION 's3://company-lake/sales/';

Hive Metastore biết bảng sales nằm ở đâu và được chia theo sale_date.

Nhưng với mô hình bảng Hive truyền thống, partition thường được biểu diễn trực tiếp bằng cấu trúc thư mục:

1sales/
2├── sale_date=2026-07-23/
3├── sale_date=2026-07-24/
4└── sale_date=2026-07-25/

Một ngày có thể ổn.

Một tháng có thể ổn.

Nhưng nếu chia theo:

  • Ngày.
  • Giờ.
  • Cửa hàng.
  • Công ty.
  • Khu vực.

Số lượng partition có thể phình ra khủng khiếp.

Mỗi partition lại trở thành một đối tượng cần được Metastore quản lý.

Cái hồ bắt đầu có quá nhiều biển chỉ đường.

Đọc biển đôi khi còn lâu hơn đi lấy hàng.

Lớp xử lý: Spark và Flink

Apache Spark

Spark đọc dữ liệu từ storage, biến đổi nó rồi ghi kết quả trở lại.

Spark SQL hỗ trợ nhiều nguồn dữ liệu và có thể trực tiếp đọc, ghi Parquet bằng DataFrame hoặc SQL.

Spark thường làm những việc như:

  • ETL theo batch.
  • Join dữ liệu lớn.
  • Làm sạch dữ liệu.
  • Tổng hợp.
  • Xây feature cho machine learning.
  • Ghi các bảng Bronze, Silver và Gold.

Flink mạnh ở xử lý luồng dữ liệu liên tục, đồng thời cũng hỗ trợ dữ liệu hữu hạn theo mô hình batch.

Ví dụ:

  • Đơn hàng phát sinh liên tục.
  • Tồn kho thay đổi liên tục.
  • Giá sản phẩm thay đổi liên tục.

Kafka đưa sự kiện vào.

Flink xử lý rồi ghi những thay đổi xuống Data Lake.

Spark giống đoàn xe tải đến kho theo chuyến.

Flink giống băng chuyền không ngừng chạy.

Lớp truy vấn: Hive, Presto và Trino

Spark xử lý dữ liệu rất tốt, nhưng không phải lúc nào người dùng cũng muốn chờ một job Spark khởi động.

Các công cụ SQL phân tán như Presto và Trino được dùng để truy vấn trực tiếp dữ liệu trong hồ.

Trino là một distributed SQL query engine, được thiết kế để truy vấn những tập dữ liệu lớn nằm trên một hoặc nhiều nguồn dữ liệu khác nhau. Nó sử dụng coordinator để lập kế hoạch và các worker để xử lý dữ liệu song song.

Người dùng có thể viết:

1SELECT
2    province,
3    SUM(revenue) AS total_revenue
4FROM lakehouse.gold_daily_sales
5WHERE sale_date >= DATE '2026-07-01'
6GROUP BY province;

Trino tìm các file cần đọc, chia công việc cho worker, xử lý song song rồi trả kết quả.

Lớp thu thập dữ liệu: Kafka và Debezium

Kafka là nền tảng event streaming phân tán, cho phép đọc, ghi, lưu và xử lý các sự kiện trên nhiều máy.

Trong bán lẻ, Kafka có thể vận chuyển:

  • Đơn hàng mới.
  • Thay đổi tồn kho.
  • Thay đổi giá.
  • Hành vi click.
  • Log ứng dụng.
  • Trạng thái giao hàng.

Nếu cần lấy thay đổi từ database nghiệp vụ, Debezium thường được đặt cạnh Kafka để thực hiện Change Data Capture.

Ví dụ một đơn hàng trong PostgreSQL được cập nhật:

1status: CREATED → PAID

Thay vì mỗi đêm đọc lại toàn bộ bảng đơn hàng, CDC phát ra một sự kiện mô tả phần đã thay đổi.

Tuy nhiên, Kafka chỉ mang sự kiện đến hồ.

Nó không tự biến những sự kiện đó thành một bảng nhất quán.

Ai đó vẫn phải giải quyết:

  • Sự kiện đến trùng.
  • Sự kiện đến sai thứ tự.
  • Dữ liệu đến trễ.
  • Bản ghi bị cập nhật.
  • Bản ghi bị xóa.
  • Hai job cùng ghi một bảng.

Đây là nơi table format bắt đầu trở nên quan trọng.

Lớp điều phối: Apache Airflow

Một pipeline thường không chỉ có một bước.

Ví dụ:

 1Lấy dữ liệu ERP
 2 3Kiểm tra dữ liệu
 4 5Ghi Bronze
 6 7Chuyển sang Silver
 8 9Tổng hợp Gold
1011Cập nhật dashboard

Airflow cho phép mô tả workflow dưới dạng DAG, trong đó mỗi task là một đơn vị công việc và các mối phụ thuộc xác định thứ tự thực thi.

Airflow không xử lý hàng tỷ dòng thay Spark.

Nó giống người quản đốc.

Nó gọi đúng đội vào đúng giờ, kiểm tra bước nào thất bại và quyết định có chạy lại hay không.

Vấn đề bắt đầu xuất hiện

Kiến trúc Data Lake đời đầu hoạt động khá tốt khi:

  • Chủ yếu ghi thêm dữ liệu.
  • Ít cập nhật.
  • Ít engine cùng ghi.
  • Số lượng partition chưa quá lớn.
  • Mỗi pipeline biết rõ mình sở hữu thư mục nào.

Nhưng khi hệ thống lớn lên, nhiều vết nứt xuất hiện.

Ghi nửa chừng

Giả sử một Spark job cần ghi 1.000 file.

Nó ghi được 800 file thì chết.

Lúc này, người đọc nhìn thấy gì?

  • 800 file mới?
  • Bộ dữ liệu cũ?
  • Một hỗn hợp giữa cũ và mới?

Nếu không có cơ chế commit ở cấp bảng, người đọc có thể thấy một trạng thái chưa hoàn chỉnh.

Nó giống siêu thị thay bảng giá giữa giờ.

Một nửa sản phẩm đã có giá mới.

Một nửa vẫn dùng giá cũ.

Khách hàng đứng ở giữa và bắt đầu suy nghĩ về luật bảo vệ người tiêu dùng.

Hai người cùng ghi

Job A đang thêm đơn hàng ngày hôm nay.

Job B đang sửa dữ liệu ngày hôm qua.

Nếu cả hai chỉ thao tác trực tiếp trên folder, mỗi job có thể không biết job kia đang làm gì.

Trong trường hợp xấu, một job ghi đè kết quả của job còn lại.

Schema thay đổi nguy hiểm

Hôm nay bảng có:

1customer_id
2customer_name
3province

Ngày mai đội dữ liệu đổi tên province thành city.

Nếu hệ thống nhận diện cột chỉ bằng tên hoặc vị trí, thay đổi schema có thể làm dữ liệu cũ bị đọc nhầm.

Iceberg gán định danh riêng cho từng field, giúp thao tác thêm, xóa, đổi tên và sắp xếp lại cột an toàn hơn mà không phải lập tức ghi lại toàn bộ dữ liệu.

Partition bị lộ vào câu truy vấn

Giả sử dữ liệu được chia theo:

1year=2026/month=07/day=25

Người dùng phải hiểu cấu trúc vật lý đó để viết truy vấn đúng.

Nếu sau này muốn đổi từ partition theo ngày sang partition theo giờ, rất nhiều pipeline và câu SQL có thể bị ảnh hưởng.

Người hỏi chỉ muốn:

1WHERE event_time >= TIMESTAMP '2026-07-25 10:00:00'

Họ không nên cần biết dữ liệu được cất theo ngày, giờ hay bucket.

Liệt kê quá nhiều file

Một bảng lớn có thể chứa hàng triệu file.

Muốn chạy truy vấn, engine phải biết file nào cần đọc.

Nếu phải gọi object storage để liệt kê từng folder và từng object, thời gian lập kế hoạch có thể trở thành nút thắt trước khi việc đọc dữ liệu thật sự bắt đầu.

Xóa và cập nhật rất đắt

File Parquet không hoạt động như một trang trong database quan hệ.

Không thể đơn giản đi đến dòng số 7.521 rồi sửa một ô.

Thông thường, hệ thống phải:

  • Đọc file cũ.
  • Tạo file mới.
  • Loại file cũ khỏi bảng.
  • Đảm bảo người đọc không nhìn thấy trạng thái ở giữa.

Data Lake đời đầu rất giỏi chứa file lớn bất biến.

Nó không tự nhiên giỏi thay đổi dữ liệu như database.

Apache Iceberg thật sự là gì?

Apache Iceberg là một open table format dành cho những bảng dữ liệu phân tích rất lớn. Nó được phát triển ban đầu tại Netflix để giải quyết các vấn đề dữ liệu quy mô lớn, sau đó được đóng góp vào Apache Software Foundation năm 2018.

Iceberg không phải:

  • Một database.
  • Một data warehouse.
  • Một query engine.
  • Một hệ thống lưu trữ.
  • Một định dạng file thay thế Parquet.
  • Một công cụ ETL.

Iceberg là một bộ quy tắc và cấu trúc metadata mô tả:

  • Bảng đang gồm những file nào.
  • Schema hiện tại là gì.
  • Partition được tổ chức ra sao.
  • Trạng thái hiện tại là snapshot nào.
  • Những snapshot cũ nào còn tồn tại.
  • File nào có thể chứa dữ liệu phù hợp với câu truy vấn.
  • Một thay đổi được công bố cho người đọc như thế nào.

Ta có thể hình dung:

1Spark / Flink / Trino
23       Iceberg
45Parquet / ORC / Avro
67 HDFS / S3 / ADLS / GCS

Iceberg đứng giữa compute engine và file.

Nó không chở hàng.

Nó cũng không xây nhà kho.

Nó giữ cuốn sổ chính xác mô tả hàng nào đang nằm trong bảng.

Những thành phần nhỏ nhất của một bảng Iceberg

Một bảng Iceberg thường được tạo nên từ nhiều tầng metadata.

 1Catalog
 2 3Metadata JSON
 4 5Snapshot
 6 7Manifest List
 8 9Manifest Files
1011Data Files

Catalog

Catalog là điểm bắt đầu để tìm bảng.

Nó giữ tên bảng và vị trí metadata hiện tại.

Ví dụ:

1catalog: lakehouse
2namespace: sales
3table: orders

Khi một engine hỏi bảng lakehouse.sales.orders, catalog giúp nó tìm file metadata đang đại diện cho trạng thái hiện tại.

Iceberg có thể làm việc với nhiều loại catalog. Trong kiến trúc đời đầu, Hive Metastore thường được sử dụng vì nó đã tồn tại sẵn trong hệ sinh thái Hadoop.

Trino mô tả catalog của Iceberg là nơi giữ một lượng metadata nhỏ, trong khi phần lớn metadata của bảng nằm trong các file trên object storage.

Metadata file

Metadata file thường ở dạng JSON.

Nó chứa:

  • Schema.
  • Partition specification.
  • Thuộc tính bảng.
  • Danh sách snapshot.
  • Snapshot hiện tại.
  • Lịch sử metadata trước đó.

Mỗi lần bảng thay đổi, Iceberg tạo một metadata file mới thay vì sửa trực tiếp file cũ.

Điều này giống việc không tẩy xóa sổ cái cũ.

Ta viết một trang sổ cái mới rồi chuyển dấu đánh dấu “trang hiện tại” sang trang mới.

Snapshot

Snapshot đại diện cho một trạng thái hoàn chỉnh của bảng tại một thời điểm.

Ví dụ:

1Snapshot 101: dữ liệu đến 08:00
2Snapshot 102: thêm đơn hàng đến 09:00
3Snapshot 103: sửa dữ liệu trả hàng

Người đọc đang dùng snapshot 102 có thể tiếp tục đọc ổn định, ngay cả khi snapshot 103 vừa được tạo ra.

Snapshot không nhất thiết sao chép toàn bộ dữ liệu.

Nó chủ yếu mô tả bộ file nào thuộc trạng thái đó.

Nhiều snapshot có thể cùng tham chiếu đến một số file giống nhau.

Manifest list

Một bảng rất lớn có thể có hàng nghìn manifest.

Manifest list là danh sách các manifest thuộc một snapshot.

Nó còn chứa thông tin tóm tắt để engine loại bỏ những manifest chắc chắn không liên quan.

Manifest file

Manifest chứa danh sách data file và metadata của từng file, chẳng hạn:

  • Đường dẫn.
  • Partition.
  • Số lượng record.
  • Kích thước.
  • Giá trị nhỏ nhất và lớn nhất của một số cột.
  • File được thêm hoặc loại ở snapshot nào.

Ví dụ một file có thống kê:

1sale_date:
2    min = 2026-07-01
3    max = 2026-07-03
4
5store_id:
6    min = 1
7    max = 50

Nếu truy vấn hỏi cửa hàng 200 trong ngày 25 tháng 7, engine biết file này không thể chứa kết quả và bỏ qua nó.

Iceberg sử dụng metadata để loại các metadata file không cần thiết, sau đó tiếp tục loại những data file không thể chứa dữ liệu phù hợp.

Data file

Đây mới là nơi chứa dữ liệu thật.

Thông thường là:

  • Parquet.
  • ORC.
  • Avro.

Iceberg không nhét toàn bộ dữ liệu vào metadata.

Nó quản lý tập hợp các file dữ liệu.

Nếu Parquet là những chiếc thùng, manifest là danh sách hàng trong một khu vực, snapshot là biên bản chốt kho, còn catalog là quầy tra cứu địa chỉ kho.

Một lần ghi dữ liệu diễn ra như thế nào?

Giả sử Spark muốn thêm dữ liệu đơn hàng.

Bước 1: Đọc trạng thái hiện tại

Spark lấy metadata hiện tại từ catalog.

Ví dụ:

1Current metadata: v105.metadata.json
2Current snapshot: 105

Bước 2: Ghi data file mới

Spark ghi các file Parquet mới xuống object storage.

1data/order-0001.parquet
2data/order-0002.parquet
3data/order-0003.parquet

Ở thời điểm này, file đã tồn tại nhưng chưa nhất thiết được xem là một phần của bảng.

Người đọc vẫn đang nhìn snapshot cũ.

Bước 3: Tạo manifest và snapshot mới

Spark tạo:

  • Manifest mới.
  • Manifest list mới.
  • Snapshot mới.
  • Metadata JSON mới.

Ví dụ:

1v106.metadata.json

Bước 4: Công bố phiên bản mới

Cuối cùng, catalog đổi con trỏ từ:

1v105.metadata.json

sang:

1v106.metadata.json

Việc đổi con trỏ hiện tại được thực hiện như một thao tác tất-cả-hoặc-không-gì. Người đọc hoặc thấy metadata cũ, hoặc thấy metadata mới; họ không phải đọc một bảng đang được cập nhật dở dang. Đây là nền tảng giúp Iceberg cung cấp khả năng cô lập giao dịch ở cấp bảng.

Nếu một writer khác đã cập nhật bảng trước đó, writer hiện tại phát hiện xung đột và phải kiểm tra lại thay vì âm thầm ghi đè.

Đây là điểm khác biệt rất lớn so với việc hai job tự do sửa cùng một folder.

Hidden partitioning: đừng bắt người dùng nhớ cách xếp kho

Trong Hive truyền thống, partition thường được lộ ra như một cột vật lý:

1WHERE sale_year = 2026
2  AND sale_month = 7
3  AND sale_day = 25

Trong Iceberg, người dùng có thể viết theo dữ liệu gốc:

1WHERE sale_time >= TIMESTAMP '2026-07-25 00:00:00'
2  AND sale_time <  TIMESTAMP '2026-07-26 00:00:00'

Iceberg biết sale_time đang được partition theo ngày và tự chuyển điều kiện truy vấn thành kế hoạch đọc phù hợp.

Tài liệu Iceberg gọi đây là hidden partitioning: câu truy vấn không cần được viết theo một layout partition cụ thể để đạt hiệu quả.

Nhờ vậy, cách chia partition có thể thay đổi theo thời gian mà không bắt buộc phải ghi lại toàn bộ dữ liệu cũ ngay lập tức.

Ví dụ:

1Dữ liệu cũ: partition theo ngày
2Dữ liệu mới: partition theo giờ

Cả hai cách tổ chức có thể cùng tồn tại trong một bảng.

Người dùng vẫn hỏi trên cột event_time.

Iceberg chịu trách nhiệm tìm đúng file.

Schema evolution: đổi nhà nhưng không đổi căn cước

Giả sử bảng có schema:

11: order_id
22: customer_id
33: province

Sau này đổi tên:

13: city

Iceberg theo dõi cột bằng field ID, không chỉ bằng tên hoặc vị trí.

Tên có thể đổi.

Vị trí có thể đổi.

Nhưng định danh của cột vẫn giữ nguyên.

Điều này giúp các thao tác sau an toàn hơn:

  • Thêm cột.
  • Xóa cột.
  • Đổi tên.
  • Sắp xếp lại thứ tự.
  • Thay đổi một số kiểu dữ liệu tương thích.
  • Thay đổi cấu trúc lồng nhau.

Iceberg hỗ trợ schema evolution mà không cần lập tức ghi lại toàn bộ data file.

Time travel: nhìn lại căn phòng trước khi bị sửa

Vì Iceberg giữ snapshot, người dùng có thể đọc trạng thái cũ của bảng.

Tính năng này hữu ích khi:

  • Một pipeline ghi sai.
  • Cần so sánh dữ liệu trước và sau.
  • Cần tái hiện báo cáo cũ.
  • Cần kiểm tra một quyết định dựa trên dữ liệu nào.
  • Cần khôi phục bảng về trạng thái trước đó.

Nhưng snapshot không miễn phí.

Metadata cũ và những data file còn được snapshot tham chiếu vẫn chiếm dung lượng.

Vì vậy, hệ thống phải có chính sách:

  • Giữ snapshot bao lâu.
  • Khi nào xóa file không còn được dùng.
  • Khi nào compact file nhỏ.
  • Làm sao tránh xóa nhầm file mà job khác vẫn cần.

Iceberg cung cấp các thủ tục bảo trì, nhưng đội dữ liệu vẫn phải chủ động vận hành chúng.

Bronze, Silver và Gold nằm ở đâu?

Bronze, Silver và Gold không phải ba tính năng của Iceberg.

Đó là cách tổ chức dữ liệu thành nhiều tầng chất lượng.

Medallion architecture mô tả dữ liệu được cải thiện dần khi đi từ Bronze sang Silver rồi Gold.

Bronze: giữ lại dấu vết ban đầu

Bronze chứa dữ liệu gần với nguồn nhất.

Ví dụ:

  • CDC từ ERP.
  • Event từ Kafka.
  • File từ đối tác.
  • Log ứng dụng.
  • JSON từ API.

Mục tiêu chính:

  • Không làm mất dữ liệu nguồn.
  • Có thể chạy lại pipeline.
  • Giữ thời gian tiếp nhận.
  • Giữ thông tin nguồn.
  • Hạn chế biến đổi nghiệp vụ.

Ví dụ:

1bronze.erp_orders
2bronze.inventory_events
3bronze.web_clicks

Bronze có thể chứa dữ liệu trùng, dữ liệu sai hoặc bản ghi đến trễ.

Nó là camera giám sát.

Camera nên ghi lại điều đã xảy ra, không nên tự chỉnh sửa hiện trường cho đẹp.

Silver: dữ liệu đã được làm sạch

Silver thực hiện:

  • Chuẩn hóa kiểu dữ liệu.
  • Loại bản ghi trùng.
  • Xử lý CDC.
  • Ghép mã sản phẩm.
  • Chuẩn hóa khách hàng.
  • Kiểm tra chất lượng.
  • Xử lý dữ liệu đến trễ.
  • Áp dụng các quy tắc cơ bản.

Ví dụ:

1silver.orders
2silver.order_items
3silver.inventory
4silver.products

Đây thường là tầng phù hợp để tạo bảng Iceberg có schema rõ, snapshot ổn định và nhiều hệ thống có thể cùng sử dụng.

Gold: dữ liệu phục vụ quyết định

Gold chứa:

  • Doanh thu theo ngày.
  • Lợi nhuận theo cửa hàng.
  • Tỷ lệ hết hàng.
  • Hiệu quả khuyến mãi.
  • Phân khúc khách hàng.
  • KPI điều hành.

Ví dụ:

1gold.daily_store_revenue
2gold.product_availability
3gold.customer_segments

Gold không nhất thiết phải chứa dữ liệu chi tiết nhất.

Nó chứa dữ liệu gần với ngôn ngữ kinh doanh nhất.

Nếu Bronze nói:

Có một event OV_CREATED.

Silver nói:

Đơn hàng số 123 đã được tạo.

Gold nói:

Doanh thu online hôm nay tăng 8%.

Một kiến trúc Lakehouse 1.0 dùng Apache Iceberg

Một kiến trúc mở, tương đối dễ hình dung có thể như sau:

 1                         ┌─────────────────────┐
 2                         │ ERP / CRM / Website │
 3                         └──────────┬──────────┘
 4 5                         CDC / Event / Batch
 6 7                   ┌────────────────┴───────────────┐
 8                   │                                │
 9             Apache Kafka                    File / API
10                   │                                │
11                   └────────────────┬───────────────┘
1213                         Spark hoặc Flink
1415                             Bronze Iceberg
1617                         Spark SQL / Flink SQL
1819                             Silver Iceberg
2021                             Spark SQL
2223                              Gold Iceberg
2425                   ┌────────────────┴───────────────┐
26                   │                                │
27                Trino                           Spark ML
28                   │                                │
29             BI / Dashboard                  Model training
30
31Storage: HDFS / S3 / ADLS / GCS
32File format: Parquet
33Table format: Apache Iceberg
34Catalog: Hive Metastore
35Orchestration: Apache Airflow

Trong kiến trúc này:

Thành phần Trách nhiệm
Kafka Vận chuyển sự kiện
Debezium Đọc thay đổi từ database
Spark Xử lý batch, ETL, join, tổng hợp
Flink Xử lý luồng liên tục
Parquet Lưu dữ liệu dạng cột trong từng file
Iceberg Quản lý bảng, snapshot, schema và partition
Hive Metastore Đăng ký và tìm bảng
Trino Truy vấn SQL tương tác
Airflow Điều phối pipeline
HDFS/S3/ADLS/GCS Giữ file vật lý
Superset/BI tool Hiển thị báo cáo

Mỗi phần mềm nên làm đúng việc của nó.

Đừng bắt Kafka làm database.

Đừng bắt Airflow xử lý một tỷ dòng.

Đừng bắt Parquet quản lý giao dịch.

Đừng bắt Iceberg thay thế hệ thống chất lượng dữ liệu.

Một kiến trúc tốt không phải kiến trúc có nhiều logo nhất.

Nó là kiến trúc mà mỗi logo có một công việc rõ ràng.

Iceberg, Hudi và Delta Lake khác nhau thế nào?

Ba cái tên thường xuất hiện cùng nhau:

  • Apache Iceberg.
  • Apache Hudi.
  • Delta Lake.

Cả ba đều cố gắng đưa những khả năng giống bảng trong database lên Data Lake, nhưng chúng có lịch sử và điểm tập trung khác nhau.

Đặc điểm Apache Iceberg Apache Hudi Delta Lake
Trọng tâm ban đầu Bảng phân tích cực lớn, nhiều engine Upsert, dữ liệu tăng dần, table services Lakehouse gắn mạnh với hệ sinh thái Spark
Metadata Snapshot, manifest list, manifest Timeline, file groups, index Transaction log
File phổ biến Parquet, ORC, Avro Chủ yếu Parquet Chủ yếu Parquet
Điểm mạnh nổi bật Đặc tả mở, hidden partitioning, multi-engine CDC, upsert, incremental processing Trải nghiệm đơn giản với Spark
Phù hợp Analytics lớn, nhiều engine cùng sử dụng Pipeline thay đổi liên tục, cập nhật nhiều Đội tập trung Spark và hệ sinh thái Delta

Apache Hudi tự mô tả là một nền tảng Data Lakehouse mở, xây trên table format hiệu năng cao và tập trung mạnh vào xử lý dữ liệu tăng dần.

Delta Lake cung cấp giao dịch, metadata có khả năng mở rộng và khả năng kết hợp xử lý batch với streaming trên các Data Lake như S3, ADLS, GCS và HDFS.

Apache Iceberg nhấn mạnh khả năng để Spark, Trino, Flink, Presto, Hive và Impala làm việc an toàn trên cùng một bảng.

Không có lựa chọn nào thắng trong mọi trường hợp.

Cần hỏi:

  • Ai ghi dữ liệu?
  • Ai đọc dữ liệu?
  • Tỷ lệ cập nhật cao hay thấp?
  • Có bao nhiêu compute engine?
  • Có cần truy vấn tăng dần không?
  • Đội hiện tại mạnh Spark hay Flink?
  • Có chấp nhận phụ thuộc một hệ sinh thái hay không?
  • Khả năng vận hành hiện tại đến đâu?

Chọn table format giống chọn nền móng.

Đổi lại sau này không phải không thể.

Nhưng chắc chắn không vui.

Những việc nên làm khi triển khai Iceberg

Bắt đầu từ một bảng có vấn đề thật

Không cần chuyển toàn bộ Data Lake sang Iceberg ngay trong một đêm.

Nên chọn một bảng đang gặp các vấn đề rõ ràng:

  • Nhiều partition.
  • Thường xuyên cập nhật.
  • Nhiều engine cùng đọc.
  • Cần quay lại phiên bản cũ.
  • Schema thay đổi thường xuyên.
  • Query planning chậm vì quá nhiều file.

Ví dụ:

1orders
2inventory_events
3product_price_history
4customer_interactions

Chuyển một bảng, đo kết quả rồi mới mở rộng.

Chọn catalog rõ ràng

Trong kiến trúc thế hệ đầu, Hive Metastore là lựa chọn dễ hiểu nếu hệ thống đã dùng Hive hoặc Trino.

Cần xác định:

  • Catalog chạy ở đâu.
  • Database phía sau có HA không.
  • Ai được tạo bảng.
  • Ai được cập nhật metadata.
  • Backup thế nào.
  • Nếu catalog mất thì khôi phục ra sao.

Data file còn nguyên nhưng catalog mất vẫn là một tình huống rất đau đầu.

Giống như nhà vẫn còn, nhưng sổ địa chính cháy mất.

Thiết kế partition theo truy vấn

Không nên partition theo mọi cột thường xuất hiện trong báo cáo.

Hãy xem:

  • Cột nào xuất hiện thường xuyên trong bộ lọc.
  • Độ phân tán của giá trị.
  • Mỗi partition sẽ chứa bao nhiêu dữ liệu.
  • Có tạo ra quá nhiều file nhỏ không.
  • Dữ liệu đến theo thời gian nào.

Ví dụ bảng đơn hàng có thể partition theo ngày:

1PARTITIONED BY (days(order_time))

Một bảng sự kiện rất lớn có thể dùng:

1PARTITIONED BY (
2    days(event_time),
3    bucket(32, customer_id)
4)

Partition quá thô khiến truy vấn đọc nhiều dữ liệu.

Partition quá mịn tạo ra vô số file nhỏ.

Không có một con số thần kỳ áp dụng cho mọi công ty.

Kiểm soát kích thước file

Một triệu file 10 KB vẫn có thể chứa ít dữ liệu hơn một file 10 GB, nhưng lại tạo ra gánh nặng metadata và lập kế hoạch khủng khiếp.

Pipeline streaming thường dễ tạo file nhỏ vì nó ghi liên tục.

Cần có chiến lược:

  • Điều chỉnh checkpoint hoặc trigger.
  • Gom đủ dữ liệu trước khi ghi.
  • Compact file định kỳ.
  • Theo dõi số lượng file trên mỗi partition.
  • Theo dõi kích thước file trung bình.

Iceberg quản lý file tốt hơn table Hive truyền thống.

Nhưng nó không làm file nhỏ tự biến mất vì lòng tốt.

Lập lịch bảo trì

Cần vận hành định kỳ:

  • Compact data file.
  • Xóa snapshot quá cũ.
  • Xóa file không còn được tham chiếu.
  • Làm gọn manifest.
  • Cập nhật statistics khi cần.
  • Theo dõi metadata tăng trưởng.

Một hệ thống giữ mọi snapshot mãi mãi sẽ ngày càng nặng.

Một hệ thống xóa quá sớm sẽ mất khả năng điều tra và khôi phục.

Chính sách retention phải dựa trên:

  • Nhu cầu kiểm toán.
  • Khả năng chạy lại pipeline.
  • Thời gian phát hiện sự cố.
  • Chi phí storage.
  • SLA phục hồi.

Tách rõ Bronze, Silver và Gold

Đừng để một bảng vừa là dữ liệu thô, vừa là dữ liệu chuẩn hóa, vừa là báo cáo.

Mỗi tầng nên có hợp đồng riêng:

Tầng Mức biến đổi Được cập nhật? Người dùng chính
Bronze Tối thiểu Chủ yếu ghi thêm Data engineer
Silver Làm sạch, chuẩn hóa Data engineer, analyst
Gold Logic kinh doanh BI, quản lý, sản phẩm

Việc tách tầng giúp:

  • Dễ chạy lại.
  • Dễ điều tra lỗi.
  • Dễ kiểm soát chất lượng.
  • Dễ xác định ai chịu trách nhiệm.
  • Tránh để một thay đổi nghiệp vụ phá toàn bộ dữ liệu nguồn.

Những điều không nên làm

Không xem Iceberg là database giao dịch

Iceberg được thiết kế cho bảng phân tích lớn.

Nó không phải lựa chọn mặc định cho:

  • Điểm bán hàng cần phản hồi vài mili giây.
  • Kiểm tra số dư tài khoản.
  • Khóa một dòng đơn hàng.
  • Cập nhật một record rồi đọc lại ngay.
  • Hàng nghìn giao dịch nhỏ mỗi giây theo kiểu OLTP.

Database nghiệp vụ vẫn nên nằm ở PostgreSQL, Oracle, MySQL hoặc hệ thống phù hợp khác.

Iceberg nhận dữ liệu để phân tích.

Nó không nhất thiết thay thế nguồn phát sinh dữ liệu.

Không cho nhiều job tự sửa file bên dưới

Một khi bảng đã được Iceberg quản lý, không nên:

  • Tự xóa file Parquet bằng script.
  • Tự đổi tên folder.
  • Tự di chuyển data file.
  • Ghi file mới vào thư mục rồi nghĩ bảng sẽ tự nhận.
  • Chạy lệnh dọn rác mà không hiểu snapshot.

Iceberg biết bảng thông qua metadata.

Một file nằm trong folder chưa chắc thuộc bảng.

Một file không còn trong snapshot hiện tại chưa chắc đã có thể xóa ngay.

Can thiệp trực tiếp vào storage giống vào kho hàng ban đêm rồi tự ý chuyển container mà không cập nhật vận đơn.

Sáng hôm sau, mọi người vẫn tìm theo sổ cũ.

Không đưa mọi thứ vào một bảng khổng lồ

Iceberg xử lý bảng lớn tốt không có nghĩa tất cả dữ liệu nên nhét vào một bảng.

Cần tách theo:

  • Miền nghiệp vụ.
  • Quyền sở hữu.
  • Chu kỳ dữ liệu.
  • Chính sách bảo mật.
  • Mô hình truy vấn.
  • Thời gian lưu trữ.

Một bảng khổng lồ chứa đơn hàng, tồn kho, hành vi khách hàng và log hệ thống không phải Lakehouse.

Đó là một nồi lẩu dữ liệu.

Không bỏ qua data quality

Iceberg đảm bảo một snapshot được công bố trọn vẹn.

Nó không đảm bảo dữ liệu bên trong đúng.

Một bảng vẫn có thể chứa:

  • Doanh thu âm.
  • Mã sản phẩm không tồn tại.
  • Đơn hàng trùng.
  • Ngày giao hàng trước ngày đặt.
  • Tồn kho lớn hơn sức chứa của kho.
  • Khách hàng 300 tuổi.

Giao dịch tốt không cứu được dữ liệu sai về nghiệp vụ.

Vẫn cần:

  • Data contract.
  • Validation.
  • Quality rules.
  • Monitoring.
  • Lineage.
  • Owner.
  • Quy trình xử lý lỗi.

Không triển khai quá nhiều phần mềm cùng lúc

Một số sơ đồ Lakehouse có đủ:

  • Kafka.
  • Debezium.
  • Flink.
  • Spark.
  • Iceberg.
  • Trino.
  • Airflow.
  • Hive Metastore.
  • Superset.
  • Ranger.
  • Atlas.
  • Kubernetes.

Nhìn rất hùng vĩ.

Mỗi logo lại cần:

  • Máy chủ.
  • Cấu hình.
  • Monitoring.
  • Backup.
  • Nâng cấp.
  • Bảo mật.
  • Người biết sửa lúc 2 giờ sáng.

Nếu dữ liệu chỉ chạy batch mỗi đêm, có thể chưa cần Flink.

Nếu chưa có CDC, chưa cần Kafka.

Nếu chỉ Spark đọc dữ liệu, chưa chắc cần Trino ngay.

Kiến trúc nên phát triển theo vấn đề.

Không nên phát triển theo số lượng logo có thể đặt lên slide.

Apache Iceberg không giải quyết những gì?

Iceberg giải quyết rất tốt lớp bảng.

Nhưng nó không tự giải quyết toàn bộ Lakehouse.

Nó không tự làm:

  • Thu thập dữ liệu.
  • CDC.
  • Điều phối pipeline.
  • Kiểm tra chất lượng.
  • Phân quyền toàn hệ thống.
  • Data lineage hoàn chỉnh.
  • Semantic layer.
  • Dashboard.
  • Tối ưu cluster Spark.
  • Thiết kế mô hình dữ liệu.
  • Định nghĩa KPI kinh doanh.
  • Dọn file nhỏ nếu không được yêu cầu.

Ta có thể viết:

$$ Lakehouse = Storage

  • FileFormat
  • TableFormat
  • Catalog
  • Compute
  • Ingestion
  • Orchestration
  • Governance
  • Consumption $$

Iceberg chỉ là một thành phần.

Nhưng đó là thành phần nằm đúng giữa rất nhiều dòng chảy.

Nếu nó hoạt động tốt, Spark, Flink và Trino có thể nhìn cùng một bảng theo cùng một sự thật.

Nếu lớp bảng yếu, mỗi engine có thể nhìn thấy một phiên bản khác nhau của cái hồ.

Tổng kết

Data Lake đời đầu giải quyết một bài toán rất lớn:

Làm sao đưa khối lượng dữ liệu khổng lồ về một nơi lưu trữ rẻ và có thể mở rộng?

Nó làm được điều đó bằng HDFS, object storage, Parquet, Hive, Spark và các query engine phân tán.

Nhưng khi dữ liệu lớn hơn, thay đổi nhiều hơn và có nhiều công cụ cùng truy cập, chỉ lưu file là chưa đủ.

Chúng ta cần biết:

  • File nào thuộc bảng.
  • Phiên bản hiện tại là gì.
  • Thay đổi nào đã hoàn tất.
  • Schema đã phát triển ra sao.
  • Partition đang được tổ chức thế nào.
  • File nào thật sự cần đọc.
  • Làm sao quay lại trạng thái cũ.

Apache Iceberg đặt một lớp quản lý bảng lên trên những file vốn dĩ không biết mình thuộc về bảng nào.

Phía dưới vẫn là Parquet.

Phía dưới nữa vẫn là HDFS hoặc object storage.

Phía trên vẫn là Spark, Flink và Trino.

Iceberg không thay thế mọi thứ.

Nó khiến mọi thứ có thể phối hợp với nhau bằng một cuốn sổ chung.

Và đó chính là bước chuyển quan trọng từ Data Lake sang Lakehouse thế hệ đầu.

Không phải vì cái hồ bỗng nhiên biến thành database.

Mà vì trên mặt hồ đã xuất hiện một hệ thống đủ chặt chẽ để dữ liệu không còn chỉ là hàng triệu file nằm im trong bóng tối.

Mỗi file có địa chỉ.

Mỗi thay đổi có lịch sử.

Mỗi bảng có một trạng thái hiện tại.

Mỗi engine có thể đọc cùng một sự thật.

Trong thế giới dữ liệu, đôi khi thứ biến một nhà kho thành một hệ thống không phải là xây thêm kho.

Mà là cuối cùng cũng có một cuốn sổ biết chính xác bên trong đang chứa những gì.

Tham khảo

Bình luận