Khái niệm Partitioning trong Iceberg và Chiến lược Transform
Trong Iceberg, Partitioning không chỉ là chia nhỏ dữ liệu trên hệ thống file mà còn là cơ chế metadata để engine truy vấn (như Trino) loại bỏ các file không cần đọc (Partition Pruning).
Iceberg hỗ trợ 3 loại Transform chính: Identity (chưa xử lý), Bucket (hash), và Truncate hoặc Bucket cho ngày tháng (Range/Date).
1. Cấu hình Partitioning với chiến lược Identity và Bucket
Bước 1: Tạo bảng mới với chiến lược partitioning kết hợp. Chúng ta sẽ partition theo year (sử dụng transform bucket hoặc identity tùy thuộc vào tính chất) và user_id (hash bucket).
Việc chọn Bucket hash giúp cân bằng kích thước các partition, tránh hiện tượng "skew" khi một giá trị có quá nhiều dữ liệu.
CREATE TABLE iceberg_demo.sales (
order_id BIGINT,
user_id BIGINT,
amount DECIMAL(10, 2),
order_date TIMESTAMP,
region STRING
)
PARTITIONED BY (
bucket(16, user_id),
year(order_date)
);
Kết quả mong đợi: Câu lệnh chạy thành công. Khi bạn list file trong storage (S3/GCS/NFS), bạn sẽ thấy cấu trúc thư mục dạng data/bucket-00000000000000000000-.../year-2023-.../.
2. Thay đổi Partitioning cho bảng hiện có (Evolution)
Bước 2: Nếu bảng đã có dữ liệu, bạn không thể thay đổi partitioning trực tiếp bằng DROP PARTITION. Bạn phải dùng lệnh ALTER TABLE để thêm partition mới.
Thao tác này sẽ cập nhật metadata mà không cần recompute toàn bộ file ngay lập tức, nhưng dữ liệu cũ vẫn nằm trong partition cũ cho đến khi Optimize.
ALTER TABLE iceberg_demo.sales
ADD PARTITION FIELDS (
bucket(8, region)
);
Kết quả mong đợi: Lệnh chạy xong. Dữ liệu cũ vẫn giữ nguyên, nhưng dữ liệu mới ghi vào sẽ tự động được chia theo region bucket 8.
Cấu hình Z-Ordering Index để tăng tốc độ lọc
Partitioning giúp loại bỏ file ở mức độ thư mục, nhưng Z-Ordering giúp loại bỏ dữ liệu bên trong một file (File Pruning).
Z-Ordering sắp xếp các giá trị của nhiều cột thành một chuỗi duy nhất, giúp các giá trị có liên quan (ví dụ: cùng user_id và cùng ngày) nằm gần nhau trong file Parquet/ORC.
1. Kích hoạt Z-Ordering khi tạo bảng
Bước 1: Khi tạo bảng mới, chỉ định các cột muốn index bằng thuộc tính write.zorder.
Chỉ nên chọn 2-3 cột có tính chọn lọc cao (high cardinality) để index. Index quá nhiều cột làm tăng thời gian ghi (write amplification).
CREATE TABLE iceberg_demo.logs (
event_id BIGINT,
user_id BIGINT,
timestamp TIMESTAMP,
action STRING,
metadata MAP
)
TBLPROPERTIES (
'write.zorder' = 'user_id, action'
);
Kết quả mong đợi: Bảng được tạo. Khi ghi dữ liệu vào, Iceberg sẽ tự động sắp xếp các row trong file Parquet theo thứ tự Z-Order của user_id và action.
2. Thêm Z-Ordering cho bảng đã tồn tại
Bước 2: Đối với bảng cũ, bạn cần kích hoạt tính năng này qua ALTER TABLE. Lưu ý: Dữ liệu cũ không tự động được sắp xếp lại ngay lập tức.
Để áp dụng cho dữ liệu cũ, bạn phải chạy lệnh OPTIMIZE (sẽ hướng dẫn ở phần sau).
ALTER TABLE iceberg_demo.logs
SET TBLPROPERTIES (
'write.zorder' = 'user_id, action'
);
Kết quả mong đợi: Thuộc tính được cập nhật. Các lần ghi mới (INSERT/UPDATE) sẽ áp dụng Z-Ordering.
Thực hiện Optimize (Rewrite Files) và Compaction
Đây là bước quan trọng nhất để Iceberg phát huy hiệu năng. Dữ liệu ghi vào theo thời gian thực thường tạo ra hàng nghìn file nhỏ (Small Files Problem).
Lệnh OPTIMIZE trong Iceberg sẽ thực hiện 2 việc: Rewrite Files (tái tạo file để áp dụng Z-Ordering/Partitioning mới) và Compaction (gộp nhiều file nhỏ thành file lớn).
1. Cấu hình Worker cho Optimize (Trino)
Bước 1: Nếu dùng Trino làm engine thực thi optimize, cần đảm bảo catalog Iceberg trong Trino có cấu hình worker đủ mạnh.
File cấu hình etc/catalog/iceberg.properties trong pod Trino cần được chỉnh sửa.
Đường dẫn: /etc/trino/catalog/iceberg.properties
Nội dung hoàn chỉnh:
connector.name=iceberg
iceberg.catalog-type=rest
iceberg.catalog.uri=http://iceberg-rest-catalog:8181
iceberg.catalog.io-impl=org.apache.iceberg.aws.s3.S3FileIO
iceberg.catalog.s3.endpoint=http://minio:9000
iceberg.catalog.s3.path-style-access=true
iceberg.s3.access-key=admin
iceberg.s3.secret-key=password
# Cấu hình worker để thực hiện optimize
trino.optimize.max-rewrite-files=100
trino.optimize.max-rewrite-files-bytes=1073741824
trino.optimize.max-compaction-files=1000
Kết quả mong đợi: Sau khi update file, restart pod Trino, engine Trino có thể nhận và thực thi lệnh OPTIMIZE từ SQL.
2. Thực thi lệnh OPTIMIZE để Compaction
Bước 2: Chạy lệnh để gộp các file nhỏ thành file lớn hơn, đồng thời áp dụng lại Z-Ordering cho dữ liệu cũ.
Sử dụng OPTIMIZE ... WHERE để chỉ định partition cụ thể cần optimize, tránh scan toàn bộ table.
OPTIMIZE iceberg_demo.logs
WHERE year(order_date) = 2023
USING compaction;
Kết quả mong đợi: Trino sẽ chạy job compaction. Số lượng file trong partition năm 2023 giảm mạnh, kích thước file trung bình tăng lên (ví dụ từ 1MB lên 128MB).
3. Thực thi lệnh OPTIMIZE để Rewrite (Áp dụng Z-Order)
Bước 3: Nếu bạn vừa thêm Z-Ordering cho bảng cũ, dùng lệnh rewrite để sắp xếp lại dữ liệu.
Command này sẽ đọc toàn bộ dữ liệu trong file cũ và ghi lại thành file mới theo thứ tự Z-Order đã định.
OPTIMIZE iceberg_demo.logs
WHERE year(order_date) = 2023
USING rewrite;
Kết quả mong đợi: Job chạy xong. File mới được tạo với dữ liệu đã được sắp xếp (sorted) theo user_id và action.
Đo lường hiệu năng trước và sau khi tối ưu hóa
Bước 1: Để verify kết quả, chúng ta cần so sánh số lượng file, kích thước file và thời gian truy vấn (Query Latency).
Sử dụng các câu lệnh SQL trong Trino để kiểm tra metadata của Iceberg.
1. Kiểm tra số lượng file và kích thước
Trước khi optimize, file thường nhỏ và nhiều. Sau khi optimize, file ít hơn và lớn hơn.
SELECT
count(*) as file_count,
sum(file_size_in_bytes) as total_size_bytes,
avg(file_size_in_bytes) as avg_file_size
FROM iceberg_demo.logs.files
WHERE year(order_date) = 2023;
Kết quả mong đợi: Sau khi chạy OPTIMIZE, file_count giảm đáng kể, avg_file_size tăng lên (tối ưu cho HDFS/S3).
2. Đo lường thời gian truy vấn (Benchmark)
Bước 2: Chạy cùng một câu truy vấn lọc dữ liệu (Filter) trước và sau khi optimize để thấy sự khác biệt về Scan Time.
Đảm bảo câu truy vấn sử dụng các cột đã được Z-Ordered (ví dụ: user_id và action).
EXPLAIN (ANALYZE)
SELECT *
FROM iceberg_demo.logs
WHERE user_id = 12345
AND action = 'click'
AND year(order_date) = 2023;
Kết quả mong đợi:
- Before: Trino phải scan nhiều file nhỏ, thời gian "Total time" cao, số lượng rows đọc (Rows read) lớn.
- After: Nhờ Z-Ordering, Trino chỉ đọc các file chứa đúng user_id và action. Số lượng rows đọc giảm mạnh (File Pruning), thời gian query giảm từ vài giây xuống vài mili-giây.
3. Kiểm tra tính năng Z-Order trong metadata
Bước 3: Xác nhận rằng file mới đã được ghi với Z-Ordering.
SELECT
file_path,
z_order_columns
FROM iceberg_demo.logs.files
WHERE year(order_date) = 2023
LIMIT 5;
Kết quả mong đợi: Cột z_order_columns hiển thị giá trị [user_id, action] cho các file mới tạo sau khi optimize.
Điều hướng series:
Mục lục: Series: Xây dựng nền tảng Data Fabric hiện đại với Apache Iceberg, Trino và Kubernetes
« Phần 5: Tối ưu hóa hiệu năng với Partitioning và Indexing trong Iceberg
Phần 6: Bảo mật và kiểm soát truy cập cho nền tảng Data Fabric »