Không có gì chỉ là một file Parquet
Giả sử nhà mình có một cái kho.
Lúc đầu kho chỉ chứa vài chục thùng mì tôm, vài cái ghế nhựa và một chiếc quạt cũ không biết còn chạy hay không. Mình có thể nhớ mọi thứ bằng đầu:
- Thùng mì nằm bên trái.
- Ghế nhựa nằm cạnh cửa.
- Cái quạt nằm đâu đó phía sau.
- Còn cục sạc Nokia thì đã biến mất khỏi thế giới vật chất.
Khi cần lấy đồ, mình chỉ việc mở cửa kho rồi nhìn quanh.
Nghe thì có vẻ đơn giản.
Nhưng nếu cái kho ấy lớn bằng một thành phố, chứa hàng tỷ thùng hàng và mỗi ngày có hàng nghìn người cùng chạy vào tìm đồ, câu chuyện bắt đầu không còn vui nữa.
Một người cần tìm hóa đơn tháng trước.
Một người khác muốn cập nhật trạng thái đơn hàng.
Một chương trình Machine Learning đang cố đọc vài tỷ dòng dữ liệu hành vi.
Trong khi đó, một ông nào đó vừa lén đặt thêm 20.000 thùng vào góc kho nhưng quên cập nhật sổ quản lý.
Lúc này, vấn đề không còn là “dữ liệu được lưu bằng Parquet hay CSV”.
Vấn đề là cái kho có biết chính xác nó đang chứa những gì hay không.
Data Lake của Grab từng đối mặt với một phiên bản rất lớn của câu chuyện đó. Hệ thống chứa dữ liệu ở quy mô petabyte, trải trên hàng tỷ object trong Amazon S3. Phần lớn các bảng được lưu dưới dạng Hive Parquet, quản lý bằng Hive Metastore và tổ chức theo thư mục. Kiến trúc này hoạt động tốt trong nhiều năm, nhưng khi số lượng dữ liệu, partition và workload tăng lên, cái kho bắt đầu kêu cót két. (Grab Tech)
Grab quyết định chuyển dần sang Apache Iceberg.
Nhưng điểm đáng học nhất trong hành trình này không phải là câu:
Iceberg nhanh hơn Hive.
Câu đó đúng trong một số trường hợp, nhưng quá đơn giản.
Bài học thật sự là: thay table format không chỉ là thay cách lưu file; nó là thay cách cả tổ chức nhìn nhận, vận hành và chịu trách nhiệm với dữ liệu.
Khi Data Lake lớn đến mức thư mục trở thành gánh nặng
Hive Parquet thường được hiểu như sau:
1s3://data-lake/orders/year=2026/month=07/day=28/
Trong thư mục đó có các file Parquet:
1part-00001.parquet
2part-00002.parquet
3part-00003.parquet
4...
Bên ngoài S3, Hive Metastore giữ thông tin về bảng, schema, partition và location.
Nói theo kiểu kho hàng:
- S3 là kho vật lý.
- File Parquet là các thùng hàng.
- Thư mục là các dãy kệ.
- Hive Metastore là cuốn sổ ghi vị trí hàng hóa.
Mô hình này dễ hiểu, dễ triển khai và từng là nền móng của rất nhiều Data Lake.
Nhưng nó có một vấn đề triết học hơi đau đầu: sự thật nằm ở đâu?
Sự thật nằm trong cuốn sổ Hive Metastore?
Hay sự thật nằm trong những file thực tế trên S3?
Nếu ai đó xóa file trực tiếp trên S3 nhưng không cập nhật Hive Metastore, cuốn sổ nói rằng hàng vẫn còn trong kho, trong khi cái thùng đã bốc hơi.
Nếu ai đó thêm file vào S3 nhưng quên đăng ký partition, hàng đã nằm trong kho nhưng không ai biết nó tồn tại.
Hai thực tại bắt đầu tách nhau ra.
Một thực tại nằm trong catalog.
Một thực tại nằm trên storage.
Và ở giữa hai thực tại ấy là một kỹ sư dữ liệu đang uống ly cà phê thứ tư trong ngày.
Bốn vấn đề Grab gặp phải
Grab chỉ ra bốn giới hạn lớn của kiến trúc Hive Parquet dựa trên thư mục.
Catalog trở thành nút cổ chai
Khi query một bảng có rất nhiều partition, hệ thống phải hỏi Hive Metastore và duyệt qua lượng metadata ngày càng lớn.
Chi phí lập kế hoạch truy vấn có thể tăng gần tuyến tính theo số partition. Tức là dữ liệu chưa được đọc, CPU chưa xử lý một dòng nghiệp vụ nào, nhưng chương trình đã tốn nhiều thời gian chỉ để xác định xem cần mở những file nào. (Grab Tech)
Giống như mình gọi điện đến thư viện:
Cho tôi mượn cuốn sách về Apache Iceberg.
Nhân viên trả lời:
Anh chờ em đọc tên toàn bộ ba triệu cuốn sách trước đã.
Cuốn sách có thể chỉ dày 200 trang, nhưng quá trình tìm nó mất cả buổi chiều.
Small file biến thành một đàn muỗi
Một số dataset Machine Learning của Grab có kích thước file trung bình dưới 1 MB, với hàng nghìn file trong mỗi partition. (Grab Tech)
Một file 1 MB nghe không đáng sợ.
Mười file cũng không đáng sợ.
Nhưng khi có hàng triệu file nhỏ, hệ thống phải thực hiện hàng triệu lần:
- Liệt kê object.
- Đọc metadata.
- Mở kết nối.
- Lập kế hoạch task.
- Mở file.
- Đọc một ít dữ liệu.
- Đóng file.
Nó giống như chuyển một tấn gạo bằng một triệu chiếc muỗng cà phê.
Khối lượng gạo không đổi, nhưng số lần đưa tay lên xuống đủ khiến con người suy ngẫm về ý nghĩa cuộc đời.
Không có thao tác thay đổi dữ liệu tự nhiên
Hive Parquet truyền thống không cung cấp trực tiếp mô hình transaction hoàn chỉnh cho những thao tác như:
1UPDATE
2DELETE
3MERGE
4UPSERT
Muốn cập nhật một phần dữ liệu, đội kỹ thuật thường phải tự xây dựng workflow:
- Đọc dữ liệu cũ.
- Ghép với dữ liệu mới.
- Ghi ra thư mục tạm.
- Đổi partition.
- Cập nhật catalog.
- Dọn dữ liệu cũ.
- Cầu nguyện không có job nào đọc đúng lúc chuyển giao.
Với dữ liệu đơn hàng, tồn kho, hoàn trả hoặc cập nhật muộn từ ERP, đây không phải nhu cầu hiếm gặp. Nó là chuyện xảy ra mỗi ngày.
Catalog và storage có thể không đồng bộ
Việc thay đổi trực tiếp trên S3 có thể khiến Hive Metastore không còn phản ánh đúng trạng thái vật lý của dữ liệu.
Đây là kiểu lỗi nguy hiểm vì hệ thống vẫn chạy.
Query vẫn trả kết quả.
Dashboard vẫn có biểu đồ.
Chỉ có điều biểu đồ ấy có thể đang kể một câu chuyện không đúng.
Máy tính không nổi giận khi dữ liệu sai. Nó chỉ rất bình thản tính toán sai với tốc độ cực nhanh.
Apache Iceberg thay đổi điều gì?
Apache Iceberg là một open table format dành cho các tập dữ liệu phân tích lớn. Nó không thay thế Parquet, S3 hay Spark.
Iceberg đứng ở một tầng khác.
- S3 vẫn giữ dữ liệu.
- Parquet vẫn có thể là định dạng file.
- Spark, Trino hoặc Flink vẫn là compute engine.
- Iceberg định nghĩa bảng gồm những file nào, phiên bản nào đang hợp lệ và dữ liệu được tổ chức ra sao.
Có thể hình dung một bảng Iceberg gồm nhiều tầng metadata:
1Table metadata
2 ↓
3Snapshot
4 ↓
5Manifest list
6 ↓
7Manifest files
8 ↓
9Data files
Mỗi lần ghi dữ liệu thành công, Iceberg tạo ra một snapshot mới của bảng.
Snapshot không cần sao chép toàn bộ dữ liệu. Nó chỉ mô tả trạng thái mới bằng một cây metadata liên kết đến những file đang thuộc về bảng.
Nhờ vậy, một commit có thể diễn ra theo cách gần giống thay cuốn mục lục hơn là dọn lại toàn bộ kho.
Khi mục lục mới được công bố thành công, người đọc nhìn thấy phiên bản mới.
Nếu commit thất bại giữa đường, mục lục cũ vẫn còn nguyên.
Đó là nền tảng giúp Iceberg hỗ trợ các đặc tính như:
- Atomic commit.
- Snapshot isolation.
- Time travel.
- Rollback.
- Schema evolution.
- Partition evolution.
- Optimistic concurrency.
Các manifest của Iceberg còn lưu partition information và thống kê cấp file như giá trị nhỏ nhất, lớn nhất, số lượng giá trị hoặc số lượng null. Query engine có thể dùng metadata này để loại bỏ những file chắc chắn không chứa dữ liệu cần tìm trước khi thực sự mở file. (Apache Iceberg)
Từ directory-centric sang table-centric
Sự thay đổi quan trọng nhất là chuyển từ tư duy:
Bảng là một thư mục chứa nhiều file.
sang:
Bảng là một thực thể có lịch sử, metadata, transaction và trạng thái rõ ràng.
Trong mô hình cũ, thư mục gần như là API.
Người dùng biết partition vật lý và đôi khi viết query phụ thuộc trực tiếp vào nó:
1WHERE event_date = '2026-07-28'
Trong Iceberg, partition có thể được ẩn khỏi người dùng:
1PARTITIONED BY (days(event_time))
Người dùng chỉ cần query trên trường nghiệp vụ:
1WHERE event_time >= TIMESTAMP '2026-07-28 00:00:00'
2 AND event_time < TIMESTAMP '2026-07-29 00:00:00'
Iceberg tự chuyển điều kiện đó thành điều kiện lọc partition phù hợp.
Cách partition cũng có thể thay đổi theo thời gian. Dữ liệu cũ vẫn giữ cách tổ chức cũ, còn dữ liệu mới dùng partition specification mới. Query engine hiểu cả hai mà không bắt người dùng viết lại toàn bộ câu SQL. (Apache Iceberg)
Đây là một thay đổi rất lớn.
Bởi vì layout vật lý không còn trói chặt hợp đồng logic của bảng.
Cái kho được phép thay đổi cách xếp hàng mà người mua không cần học lại đường đi trong kho.
Vì sao Grab chọn Iceberg?
Grab đánh giá nhiều open table format trước khi chọn Apache Iceberg làm định dạng mặc định.
Ba yếu tố chính được nhắc đến là:
- Mô hình quản trị cộng đồng.
- Khả năng tương thích với nhiều compute engine.
- Tính linh hoạt trong dài hạn. (Grab Tech)
Điểm này rất đáng chú ý.
Grab không tuyên bố rằng từ nay toàn bộ thế giới chỉ còn Iceberg.
Ngược lại, họ muốn kiến trúc dài hạn có khả năng không phụ thuộc cứng vào một format duy nhất.
Iceberg là lựa chọn mặc định ở thời điểm hiện tại, nhưng hệ thống vẫn phải sống chung với:
- Hive.
- Delta Lake.
- Apache Hudi.
- Những bảng chưa kịp migration.
- Những workload chưa thể chuyển đổi.
- Những engine có mức hỗ trợ format khác nhau.
Trong phòng thí nghiệm, chúng ta có thể tạo một Data Lake mới tinh, chọn một công nghệ rồi tuyên bố chiến thắng.
Trong production, quá khứ không biến mất chỉ vì kiến trúc sư vừa vẽ một sơ đồ mới.
Grab không migration toàn bộ Data Lake trong một đêm
Một Data Lake lớn không thể chuyển đổi bằng một câu lệnh:
1migrate-everything-to-iceberg --pray
Grab chọn chiến lược migration theo giá trị.
Những bảng có lợi ích lớn nhất được chuyển trước, thay vì cố đổi toàn bộ hệ thống cùng lúc. Các tiêu chí gồm tần suất scan, chi phí API và mức độ ảnh hưởng đến workload. (Grab Tech)
Kết quả được Grab công bố trên một số workload tiêu biểu khá rõ ràng.
Query nhanh hơn khoảng 10 lần
Với một dataset navigation có lưu lượng cao, việc sắp xếp dữ liệu theo Z-order giúp Trino tận dụng data skipping và thống kê min/max tốt hơn.
Thời gian chạy query giảm từ khoảng 70 giây xuống còn 6 giây. (Grab Tech)
Z-ordering cố gắng đặt những dòng có giá trị gần nhau trên nhiều chiều vào gần nhau về mặt vật lý.
Ví dụ query thường lọc theo:
1customer_id
2event_time
3site_id
Nếu dữ liệu có liên quan được gom vào một nhóm file nhỏ hơn, metadata có thể chỉ ra rằng phần lớn file không cần đọc.
Không phải CPU đột nhiên chạy nhanh gấp mười lần.
Hệ thống chỉ trở nên khôn ngoan hơn trong việc không làm những việc vô ích.
Trong data engineering, đôi khi tối ưu lớn nhất không đến từ việc chạy nhanh hơn, mà đến từ việc quyết định không chạy.
Chi phí S3 API giảm tới 95%
Với một bảng vận hành được truy vấn nhiều, Grab ghi nhận chi phí S3 API hằng ngày giảm tới 95% mà không cần thay đổi câu query. (Grab Tech)
Phần tiết kiệm đến từ:
- File lớn hơn.
- Ít object hơn.
- Không phải thực hiện object listing tốn kém trong quá trình planning.
- Metadata chỉ dẫn chính xác hơn về file cần đọc.
Đây là một điểm nhiều người bỏ quên.
Khi nói đến Data Lake, chúng ta thường chỉ nhìn chi phí lưu trữ theo TB.
Nhưng một object storage không chỉ tính tiền dung lượng.
Nó còn có thể tính tiền theo số lần:
- LIST.
- GET.
- PUT.
- HEAD.
- DELETE.
Một tỷ file nhỏ không chỉ là một tỷ mẩu dữ liệu.
Nó là một tỷ cánh cửa cần được gõ, mở rồi đóng lại.
Tài nguyên compute giảm khoảng một nửa
Một dataset phục vụ funnel analysis giảm khoảng một nửa lượng tài nguyên cluster sử dụng. Một pipeline Machine Learning khác cũng cải thiện độ mới của feature cho downstream model. (Grab Tech)
Điểm quan trọng là lợi ích của table format không dừng ở nhóm Data Platform.
Khi table planning tốt hơn, file layout hợp lý hơn và dữ liệu mới xuất hiện sớm hơn, những hệ thống phía sau cũng được hưởng lợi:
- Dashboard cập nhật nhanh hơn.
- Feature phục vụ model mới hơn.
- Job batch hoàn thành sớm hơn.
- Cluster rảnh hơn cho workload khác.
- Người dùng ít có thời gian đi pha cà phê trong lúc chờ query.
UnifiedSparkCatalog: Người dùng không nên trả giá cho migration
Sau khi Iceberg xuất hiện, Grab gặp một vấn đề mới.
Spark chỉ có thể gắn một implementation làm spark_catalog mặc định. Trong khi đó, mỗi table format thường có catalog riêng:
1iceberg_catalog.schema.table
2delta_catalog.schema.table
3hudi_catalog.schema.table
Điều này tạo ra hai vấn đề.
Thứ nhất, kỹ sư phải biết chính xác bảng đang dùng format nào.
Thứ hai, khi một bảng được migration từ Hive sang Iceberg hoặc từ format này sang format khác, những query viết cứng catalog prefix có thể bị hỏng.
Ví dụ trước migration:
1SELECT *
2FROM hive_catalog.sales.orders;
Sau migration, người dùng phải sửa thành:
1SELECT *
2FROM iceberg_catalog.sales.orders;
Một bảng có thể chỉ cần vài phút để migration.
Nhưng hàng nghìn query, notebook, dashboard và pipeline phía sau có thể mất nhiều tháng để sửa.
Grab xây dựng UnifiedSparkCatalog nhằm che giấu sự khác biệt đó.
Người dùng tiếp tục query:
1SELECT *
2FROM sales.orders;
Ở phía dưới, catalog thực hiện các bước:
- Đọc metadata của bảng.
- Phát hiện bảng thuộc Hive, Iceberg, Delta hay Hudi.
- Chuyển operation đến catalog tương ứng.
- Fallback về session catalog nếu cần.
Grab đã công bố mã nguồn của UnifiedSparkCatalog. Công cụ hỗ trợ Spark 3.4 và 3.5, tự động phát hiện format từ metadata, khởi tạo catalog theo kiểu lazy và cho phép join bảng thuộc nhiều format mà không cần prefix riêng. (GitHub)
Đây không chỉ là một tiện ích kỹ thuật.
Nó thể hiện một nguyên tắc kiến trúc quan trọng:
Một cuộc migration tốt là cuộc migration mà phần lớn người dùng không biết nó vừa xảy ra.
Những lesson learned quan trọng nhất
1. Đừng migration vì công nghệ mới trông đẹp
Iceberg mang lại nhiều lợi ích, nhưng không phải bảng nào cũng đáng được chuyển ngay lập tức.
Một bảng nhỏ, mỗi ngày chỉ đọc một lần, không có update và cũng không gặp vấn đề small file có thể chưa cần migration.
Trong khi đó, một bảng có:
- Hàng trăm nghìn partition.
- Hàng triệu file nhỏ.
- Tần suất query cao.
- Chi phí S3 LIST lớn.
- Nhu cầu
MERGE,UPDATEhoặc xử lý late-arriving data.
sẽ là ứng viên tốt hơn.
Grab ưu tiên migration dựa trên mức độ scan và chi phí API thay vì đổi toàn bộ lake cùng lúc. (Grab Tech)
Bài học ở đây là phải có baseline trước khi migration:
1Planning time
2Query runtime
3S3 API requests
4S3 API cost
5Bytes scanned
6File count
7Average file size
8Compute hours
9Failure rate
10Data freshness
Không có baseline, chúng ta rất dễ rơi vào tình huống:
Migration thành công rực rỡ, nhưng không ai biết nó thành công ở chỗ nào.
2. Table format không tự chữa được small file
Iceberg quản lý file tốt hơn, nhưng nó không thể biến một triệu file 500 KB thành file 512 MB chỉ bằng sức mạnh tinh thần.
Nếu pipeline streaming hoặc micro-batch liên tục tạo file nhỏ, chúng ta vẫn cần compaction.
Apache Iceberg khuyến nghị thực hiện các hoạt động bảo trì định kỳ như:
- Compact data files.
- Rewrite manifests.
- Expire snapshots.
- Xóa orphan files.
- Quản lý metadata cũ. (Apache Iceberg)
Ví dụ với Spark SQL:
1CALL catalog.system.rewrite_data_files(
2 table => 'analytics.customer_events'
3);
Small file không phải một lỗi xảy ra một lần.
Nó giống bụi trong nhà.
Dọn hôm nay không có nghĩa là ba tháng sau nhà vẫn sạch.
Vì vậy, khi triển khai Iceberg, maintenance phải được xem là một phần của platform:
1Ingestion
2 ↓
3Commit
4 ↓
5Monitor file size
6 ↓
7Compact khi cần
8 ↓
9Rewrite manifests
10 ↓
11Expire snapshots
12 ↓
13Delete orphan files an toàn
Nếu chỉ tạo bảng Iceberg rồi bỏ mặc, vài tháng sau metadata và file layout có thể lại trở thành một khu rừng.
3. Metadata cũng là một hệ thống production
Trong quá trình triển khai, Grab gặp tình trạng lock tồn tại bất thường trong Hive Metastore và chặn commit.
Nguyên nhân được truy ra là read timeout ở phía metastore quá thấp khi tải cao. Grab điều chỉnh retry interval và tăng timeout để giải quyết. (Grab Tech)
Bài học là metadata không phải vài file JSON vô hại nằm bên cạnh dữ liệu.
Nó nằm trên đường đi của:
- Query planning.
- Commit.
- Schema change.
- Snapshot creation.
- Table discovery.
- Lineage.
- Access control.
Nếu catalog chậm, toàn bộ Data Lake có thể chậm.
Nếu catalog không ổn định, writer có thể commit thất bại.
Nếu retry được cấu hình thiếu cẩn thận, một lỗi mạng vài giây có thể biến thành một đàn job cùng retry và giẫm lên nhau.
Cần theo dõi riêng các chỉ số như:
1Catalog request latency
2Commit latency
3Commit retry count
4Lock waiting time
5Lock age
6Failed commits
7Concurrent writers
8Metadata file count
9Manifest count
10Snapshot count
Đừng đợi đến khi người dùng báo “Spark treo” mới bắt đầu nhìn vào Hive Metastore.
4. Timestamp không phải một kiểu dữ liệu vô tội
Grab gặp vấn đề tương thích khi Spark 3.4 đưa vào TIMESTAMP_NTZ, tức timestamp không gắn múi giờ, trong khi Iceberg thường làm việc với semantics của TIMESTAMP_LTZ cho timestamp có liên quan đến múi giờ.
Sự khác biệt này ảnh hưởng đến các Hive view cũ. Grab phải xây dựng workflow migration riêng và áp dụng các thay đổi có mục tiêu ở Trino để việc cast dữ liệu nhất quán. (Grab Tech)
Nhìn bên ngoài, hai giá trị có thể giống nhau:
12026-07-28 08:00:00
Nhưng chúng có thể mang hai ý nghĩa hoàn toàn khác.
TIMESTAMP_NTZ có thể được hiểu là:
Đồng hồ trên tường chỉ 8 giờ. Không quan tâm cái tường đang ở quốc gia nào.
Trong khi timestamp có timezone semantics gần hơn với:
Đây là một thời điểm cụ thể của vũ trụ, được hiển thị thành 8 giờ theo một múi giờ nào đó.
Nếu chuyển đổi sai, dữ liệu không nổ tung.
Nó chỉ lặng lẽ lệch vài giờ.
Đơn hàng 00:30 có thể chạy sang ngày hôm trước.
Doanh thu cuối tháng có thể nhảy qua partition khác.
Dashboard vẫn đẹp, chỉ có sự thật bị nghiêng đi một chút.
Trước khi migration, cần lập bảng kiểm kê:
- Cột nào là thời điểm tuyệt đối?
- Cột nào chỉ là giờ địa phương?
- Dữ liệu cũ được ghi theo timezone nào?
- Spark session dùng timezone nào?
- Trino, Flink và các BI tool cast ra sao?
- View cũ đang ngầm chuyển đổi kiểu dữ liệu thế nào?
Timestamp migration phải được xem là một dự án kiểm thử dữ liệu, không phải một thay đổi DDL nhỏ.
5. Đọc dữ liệu cũ có thể làm hóa đơn tăng trước khi giảm
Để tạo metadata Iceberg cho dữ liệu lịch sử, hệ thống có thể phải đọc metadata hoặc truy cập lại những object cũ.
Nếu dữ liệu đang nằm trong các storage tier giá rẻ hoặc tier lạnh, việc truy cập có thể tạo ra một đợt chi phí bất thường.
Grab ghi nhận nguy cơ tăng chi phí một lần trong lúc migration và vì vậy tiếp tục ưu tiên những bảng mang lại lợi ích lớn nhất thay vì quét toàn bộ lake. (Grab Tech)
Đây là kiểu chi phí dễ bị bỏ qua trong slide kiến trúc.
Trên slide, migration chỉ là một mũi tên:
1Hive Parquet ───────────→ Apache Iceberg
Trong thế giới vật lý, mũi tên ấy có thể là:
- Hàng tỷ request đến object storage.
- Hàng nghìn giờ compute.
- Dữ liệu bị kéo ra khỏi tầng lưu trữ lạnh.
- Network traffic.
- Metadata generation.
- Compaction.
- Kiểm tra checksum.
- Chạy song song hệ thống cũ và mới.
- Đội kỹ thuật thức khuya nhìn dashboard.
Không có mũi tên kiến trúc nào thực sự miễn phí.
6. Migration format phải đi cùng migration trải nghiệm người dùng
Nếu mỗi kỹ sư phải nhớ:
1Bảng A là Hive
2Bảng B là Iceberg
3Bảng C là Delta
4Bảng D tuần trước là Hive nhưng hôm nay là Iceberg
thì sự phức tạp của platform đã bị chuyển từ máy tính sang não người.
UnifiedSparkCatalog của Grab giải quyết chính điểm này.
Nó tạo một lớp ổn định cho tên bảng trong khi định dạng vật lý phía dưới có thể thay đổi. (GitHub)
Đây là lesson learned rất đáng giá:
Đừng bắt người dùng hiểu chi tiết của cuộc migration nếu chi tiết đó không giúp họ làm việc tốt hơn.
Platform team nên cố gắng giữ ổn định:
- Tên bảng.
- Schema contract.
- Quyền truy cập.
- Cú pháp query.
- Data lineage.
- SLA dữ liệu.
- Cách phát hiện lỗi.
Format phía dưới có thể thay đổi.
Hợp đồng với người dùng thì không nên thay đổi tùy hứng.
7. Hãy thiết kế cho thời kỳ nhiều format cùng tồn tại
Một tổ chức lớn hiếm khi có thể đạt trạng thái mọi bảng đều dùng chung một format.
Sẽ luôn có:
- Bảng legacy chưa migration.
- Bảng do đối tác cung cấp.
- Workload phù hợp hơn với format khác.
- Engine chưa hỗ trợ đủ tính năng.
- Nhóm dự án chưa có thời gian chuyển đổi.
- Một bảng kỳ lạ được tạo từ năm nào đó mà không ai dám chạm vào.
Vì vậy, kiến trúc thực tế không phải:
1Hive biến mất
2Iceberg xuất hiện
Mà là:
1Hive + Iceberg + Delta + Hudi + các hệ thống cũ
2 ↓
3 Một lớp truy cập thống nhất
Grab muốn giữ khả năng format-agnostic trong dài hạn và đang quan sát những dự án như Apache XTable để cải thiện tính tương tác giữa các table format. (Grab Tech)
Đây là cách tiếp cận lành mạnh hơn việc biến một công nghệ thành tôn giáo.
Công nghệ tồn tại để phục vụ dữ liệu.
Dữ liệu không tồn tại để chứng minh công nghệ mình chọn là đúng.
Một lộ trình triển khai Iceberg thực tế
Từ hành trình của Grab, có thể rút ra một lộ trình tương đối an toàn cho các doanh nghiệp đang có Data Lake dựa trên Hive Parquet.
Bước 1: Lập bản đồ hiện trạng
Thu thập cho từng bảng:
1Dung lượng
2Số partition
3Số file
4Kích thước file trung bình
5Tần suất đọc
6Tần suất ghi
7Số lượng consumer
8Engine đang sử dụng
9Chi phí object storage API
10Nhu cầu UPDATE, DELETE, MERGE
11Yêu cầu time travel
12Mức độ quan trọng
Không nên bắt đầu bằng câu:
Bảng nào dễ migration nhất?
Nên bắt đầu bằng câu:
Bảng nào đang gây đau nhiều nhất và migration có thể đo được giá trị?
Bước 2: Chia bảng thành nhóm
Ví dụ:
- Nhóm A: Đọc nhiều, nhiều file nhỏ, chi phí cao.
- Nhóm B: Có late update, correction hoặc delete.
- Nhóm C: Dữ liệu lạnh, ít truy cập.
- Nhóm D: Legacy phức tạp, nhiều downstream dependency.
- Nhóm E: Bảng mới chưa có consumer.
Có thể bắt đầu với một số bảng thuộc nhóm A, B và E.
Nhóm E giúp kiểm chứng quy trình tạo bảng mới.
Nhóm A và B giúp chứng minh giá trị kinh tế.
Bước 3: Xây compatibility layer
Trước khi migration hàng loạt, cần trả lời:
- Tên bảng có giữ nguyên không?
- Query cũ có tiếp tục chạy không?
- Spark và Trino nhìn cùng một trạng thái bảng không?
- Access control có thay đổi không?
- Lineage có còn hoạt động không?
- BI tool có đọc được kiểu dữ liệu mới không?
- Các view Hive cũ có bị lỗi không?
Một lớp catalog thống nhất như cách Grab xây dựng có thể giảm đáng kể blast radius.
Bước 4: Chạy song song và đối chiếu
Trong thời gian đầu, có thể chạy query trên cả bảng cũ và bảng mới:
1SELECT COUNT(*) FROM hive_table;
2SELECT COUNT(*) FROM iceberg_table;
Nhưng chỉ so sánh COUNT(*) là chưa đủ.
Cần kiểm tra thêm:
1Count theo ngày
2Sum doanh thu
3Min/max timestamp
4Số lượng null
5Distinct key
6Duplicate key
7Late-arriving records
8Delete và return records
9Các partition biên tháng
10Các mốc đổi múi giờ
Đặc biệt với hệ thống bán lẻ, nên kiểm tra những tình huống khó:
- Đặt hàng cuối tháng, nhận hàng đầu tháng sau.
- Đơn bị hủy rồi tạo đơn thay thế.
- Hàng trả lại sau nhiều ngày.
- ERP cập nhật chứng từ lịch sử.
- Dữ liệu đến trễ.
- Một nghiệp vụ được sửa nhiều lần.
Bước 5: Thiết lập maintenance trước khi mở rộng
Trước khi onboarding hàng nghìn bảng, phải có sẵn:
- Chính sách target file size.
- Job compaction.
- Snapshot retention.
- Orphan file cleanup.
- Manifest rewrite.
- Cảnh báo file nhỏ.
- Cảnh báo snapshot tăng bất thường.
- Dashboard commit latency.
- Runbook khi commit conflict.
Không nên chờ hệ thống bẩn rồi mới xây đội vệ sinh.
Bước 6: Đo lại sau migration
Sau khi migration, so sánh:
$$ \text{Improvement} = \frac{\text{Before} - \text{After}} {\text{Before}} \times 100% $$
Ví dụ query giảm từ 70 giây xuống 6 giây:
$$ \frac{70 - 6}{70} \times 100% \approx 91.4% $$
Nhưng không nên chỉ đo query nhanh nhất.
Cần nhìn toàn bộ hệ thống:
- P50, P95 planning time.
- P50, P95 runtime.
- Compute cost.
- Storage API cost.
- Data freshness.
- Commit failure.
- Số lần vận hành thủ công.
- Số incident.
- Thời gian xử lý incident.
Một migration tốt không chỉ làm benchmark đẹp.
Nó phải khiến số lần con người bị gọi dậy lúc hai giờ sáng giảm xuống.
Iceberg không phải phép màu
Apache Iceberg giải quyết nhiều vấn đề mà Hive Parquet gặp phải ở quy mô lớn:
- Quản lý bảng bằng metadata thay vì phụ thuộc vào directory listing.
- Hỗ trợ snapshot và transaction.
- Cho phép schema và partition evolution.
- Hỗ trợ query planning và data skipping tốt hơn.
- Làm nền tảng cho
UPDATE,DELETE,MERGEvà time travel. - Hoạt động với nhiều compute engine.
Nhưng Iceberg không tự động giải quyết:
- Pipeline ghi dữ liệu kém.
- File nhỏ liên tục được sinh ra.
- Catalog cấu hình sai.
- Timestamp semantics không rõ ràng.
- Data contract lỏng lẻo.
- Query viết thiếu điều kiện.
- Team không có quy trình maintenance.
- Migration không có metric.
- Người dùng bị bắt nhớ quá nhiều chi tiết hạ tầng.
Đổi sang Iceberg nhưng không thay đổi cách vận hành cũng giống như mua một chiếc tủ lạnh mới rồi tiếp tục để cửa mở cả ngày.
Máy tốt hơn thật.
Nhưng tiền điện vẫn ra đi trong tiếng máy nén gầm gừ.
Tổng kết
Hành trình của Grab cho thấy Apache Iceberg không chỉ là một định dạng bảng mới đặt lên S3.
Nó là quá trình đưa khái niệm “bảng” trở lại Data Lake: một thực thể có trạng thái, lịch sử, metadata và quy tắc commit rõ ràng.
Grab đạt được những kết quả đáng chú ý trên các workload tiêu biểu: query từ khoảng 70 giây xuống 6 giây, chi phí S3 API giảm tới 95% và tài nguyên compute của một số pipeline giảm khoảng một nửa. Nhưng những con số đó không tự nhiên xuất hiện chỉ vì đổi vài dòng cấu hình. (Grab Tech)
Phía sau chúng là hàng loạt bài học ít hào nhoáng hơn:
- Migration theo giá trị, không chạy theo phong trào.
- Metadata phải được vận hành như một hệ thống production.
- Small file cần maintenance liên tục.
- Timestamp có thể phá hỏng tính đúng đắn của dữ liệu.
- Storage tier có thể tạo chi phí migration bất ngờ.
- Trải nghiệm người dùng phải được bảo vệ bằng lớp abstraction.
- Nhiều table format sẽ cùng tồn tại trong một thời gian rất dài.
Có lẽ lesson learned lớn nhất là thế này:
Một Data Lake không trở thành Lakehouse chỉ vì bên trong xuất hiện vài bảng Iceberg. Nó chỉ thực sự thay đổi khi dữ liệu được quản lý như một sản phẩm có hợp đồng, lịch sử, người chịu trách nhiệm và chi phí vật lý rõ ràng.
Bởi vì dưới một câu SQL tưởng như rất nhẹ nhàng là hàng nghìn CPU core đang thức dậy, hàng triệu metadata entry đang được duyệt, hàng tỷ electron đang chạy xuyên qua dây dẫn và những chiếc quạt trong data center đang gầm lên để đẩy nhiệt trở lại không khí.
Không có gì là hoàn toàn ảo.
Kể cả một cái bảng.
Cảm ơn các bạn đã quan tâm và theo dõi bài viết, hẹn gặp lại ở các bài viết tiếp theo.
Tham khảo
- Grab Engineering, “Scaling Grab’s Data Lake: Our journey to Apache Iceberg adoption”. (Grab Tech)
- Grab, mã nguồn UnifiedSparkCatalog. (GitHub)
- Apache Iceberg Documentation, giới thiệu và các đặc tính của table format. (Apache Iceberg)
- Apache Iceberg Documentation, hidden partitioning. (Apache Iceberg)
- Apache Iceberg Documentation, schema và partition evolution. (Apache Iceberg)
- Apache Iceberg Documentation, metadata filtering và data skipping. (Apache Iceberg)
- Apache Iceberg Documentation, maintenance, compaction và snapshot expiration. (Apache Iceberg)
Bình luận