Khái niệm Data Lakehouse và Vai trò của Apache Iceberg
Trong kiến trúc Data Lakehouse, chúng ta kết hợp tính linh hoạt của Data Lake (lưu trữ dữ liệu thô, chi phí thấp) với tính quản trị của Data Warehouse (ACID transactions, schema evolution). Apache Iceberg đóng vai trò là lớp quản lý metadata (Table Format) nằm trên các file dữ liệu thô trong object storage hoặc file system.
Khác với các định dạng file truyền thống như Parquet hay Avora chỉ lưu dữ liệu, Iceberg lưu trữ metadata riêng biệt dưới dạng file JSON. Điều này cho phép các engine truy vấn như Spark, Trino, hoặc DuckDB đọc cùng một dataset mà không cần lock toàn bộ table, đồng thời hỗ trợ Time Travel (du lịch thời gian) và ACID transactions.
Kiến trúc hoạt động của Iceberg
Iceberg chia tách dữ liệu thành 2 thành phần chính: Data Files (thường là Parquet/Avro) và Metadata Files (Snapshot, Manifest, Manifest List). Khi bạn thực hiện thao tác INSERT, Iceberg không ghi đè vào file Parquet cũ mà tạo một Snapshot mới, cập nhật Manifest List để chỉ trỏ đến file Parquet mới.
Để quản lý các Snapshot này, Iceberg cần một Catalog (Danh mục). Catalog chịu trách nhiệm lưu trữ định vị (location) của metadata file table và quản lý các thao tác DDL (Create, Drop, Alter). Trong môi trường serverless đơn giản này, chúng ta sẽ sử dụng `StaticCatalog` hoặc `FileCatalog` để lưu metadata trực tiếp vào file system thay vì dùng Hive Metastore hay REST Catalog phức tạp.
Cài đặt PyIceberg và các phụ thuộc Python
Để tương tác với Apache Iceberg từ Python, chúng ta cần thư viện `pyiceberg`. Thư viện này yêu cầu `pyarrow` để xử lý dữ liệu dạng Arrow và `fastparquet` để đọc/ghi file Parquet. Chúng ta sẽ tạo một môi trường ảo (venv) để cách ly các thư viện này khỏi hệ thống Ubuntu 24.04.
Tạo môi trường ảo và kích hoạt
Trước tiên, tạo thư mục làm việc và môi trường ảo Python. Bước này đảm bảo các gói thư viện không xung đột với các package hệ thống của Ubuntu.
mkdir -p ~/iceberg-lab && cd ~/iceberg-lab
python3 -m venv venv
source venv/bin/activate
Kết quả mong đợi: Bạn thấy dấu nhắc lệnh (prompt) thay đổi, xuất hiện `(venv)` ở đầu dòng, chứng tỏ môi trường ảo đã được kích hoạt.
Cài đặt PyIceberg và dependencies
Chúng ta sẽ cài đặt `pyiceberg` cùng với `pyarrow` và `fastparquet`. PyIceberg tự động kéo theo các dependencies cần thiết, nhưng việc cài đặt tường minh giúp đảm bảo phiên bản tương thích tốt nhất cho môi trường Ubuntu 24.04.
pip install pyiceberg pyarrow fastparquet
Kết quả mong đợi: Quá trình cài đặt hoàn tất không lỗi, hiển thị thông báo `Successfully installed pyiceberg-... pyarrow-... fastparquet-...`.
Verify cài đặt
Để chắc chắn mọi thứ đã sẵn sàng, hãy thử import các thư viện trong Python REPL và kiểm tra phiên bản.
python3 -c "import pyiceberg; import pyarrow; import fastparquet; print(f'PyIceberg: {pyiceberg.__version__}'); print(f'PyArrow: {pyarrow.__version__}'); print(f'FastParquet: {fastparquet.__version__}')"
Kết quả mong đợi: Hiển thị phiên bản của cả 3 thư viện trên 3 dòng riêng biệt mà không bị lỗi `ImportError`.
Cấu hình Catalog Iceberg sử dụng File Metadata
Trong phần này, chúng ta sẽ thiết lập một `FileCatalog`. Đây là cách đơn giản nhất để bắt đầu với Iceberg mà không cần thiết lập Hive Metastore hay dịch vụ REST Catalog. Catalog này sẽ lưu trữ toàn bộ metadata (snapshot, schema, partition spec) dưới dạng các file JSON trong một thư mục cụ thể trên disk.
Chuẩn bị thư mục lưu trữ
Chúng ta cần 2 thư mục: một cho Catalog (nơi lưu metadata của Iceberg) và một cho Data Lake (nơi lưu file Parquet thực tế). Việc tách biệt này mô phỏng cấu trúc production nơi metadata và data file có thể nằm ở các bucket S3 khác nhau hoặc cùng bucket nhưng khác path.
mkdir -p ~/iceberg-lab/catalog
mkdir -p ~/iceberg-lab/data-lake
Kết quả mong đợi: Hai thư mục `catalog` và `data-lake` được tạo thành công trong `~/iceberg-lab`.
File cấu hình Catalog
Chúng ta sẽ tạo một file cấu hình YAML để định nghĩa Catalog. File này chỉ định backend là `file`, nơi lưu trữ catalog là đường dẫn tuyệt đối, và prefix namespace (tên namespace mặc định cho các table).
Đường dẫn file cấu hình: `~/iceberg-lab/catalog.properties` (hoặc dùng file YAML tùy cách PyIceberg load, ở đây chúng ta dùng dict trong code Python cho đơn giản, nhưng để minh họa cấu hình file, chúng ta tạo file properties).
Tuy nhiên, trong Python với `FileCatalog`, chúng ta thường khởi tạo trực tiếp bằng tham số. Để minh họa việc cấu hình rõ ràng, hãy tạo một file cấu hình YAML để load sau này nếu cần mở rộng, nhưng hiện tại chúng ta sẽ cấu hình trực tiếp trong code Python để đảm bảo chạy được ngay.
Tạo file placeholder cho cấu hình (nếu dùng external config):
cat > ~/iceberg-lab/catalog_config.yaml
Kết quả mong đợi: File `catalog_config.yaml` được tạo với nội dung cấu hình đúng, đường dẫn thay đổi thành đường dẫn home của bạn (ví dụ `/home/youruser`).
Khởi tạo Catalog trong Python
Thay vì load từ file YAML phức tạp, chúng ta sẽ khởi tạo `FileCatalog` trực tiếp trong script Python để đảm bảo tính chính xác tuyệt đối khi copy-paste. Điều này xác định Catalog với tên `my_catalog`, chỉ định thư mục metadata và thư mục warehouse.
cat > ~/iceberg-lab/init_catalog.py
Chú ý quan trọng: Thay thế `home/ubuntu` bằng đường dẫn home thực tế của bạn (dùng lệnh `echo $HOME` để lấy). Ví dụ: `/home/admin` hoặc `/home/vscode`.
Chạy script để khởi tạo:
python3 ~/iceberg-lab/init_catalog.py
Kết quả mong đợi: Thông báo `Catalog initialized successfully` và hiển thị đường dẫn warehouse cùng catalog directory. Trong thư mục `~/iceberg-lab/catalog`, bạn sẽ thấy thư mục `default` được tạo.
Tạo Table Iceberg và Thao tác DML
Bây giờ chúng ta sẽ tạo một table thực tế với schema cụ thể (user_id, name, email, created_at) và định nghĩa partition (phân vùng) theo ngày. Sau đó, chúng ta sẽ thực hiện các thao tác INSERT và UPDATE để kiểm chứng tính năng ACID của Iceberg.
Định nghĩa Schema và Tạo Table
Sử dụng `pyiceberg.schema.Schema` để định nghĩa cấu trúc dữ liệu. Chúng ta sẽ tạo table `users` trong namespace `default` của catalog `my_catalog`.
cat > ~/iceberg-lab/create_table.py
Chú ý: Thay `home/ubuntu` bằng đường dẫn home của bạn. Chạy script:
python3 ~/iceberg-lab/create_table.py
Kết quả mong đợi: Thông báo `Table 'default.users' created successfully`. Trong thư mục `catalog/default`, bạn sẽ thấy file `users/metadata` chứa các file metadata (ví dụ `v1.metadata.json`).
Thực hiện thao tác INSERT (Ghi dữ liệu)
Sử dụng `pyarrow` để tạo DataFrame, sau đó dùng `table.append()` để ghi dữ liệu vào Iceberg. Thao tác này sẽ tạo một Snapshot mới.
cat > ~/iceberg-lab/insert_data.py
Chú ý: Thay `home/ubuntu` bằng đường dẫn home của bạn. Chạy script:
python3 ~/iceberg-lab/insert_data.py
Kết quả mong đợi: Thông báo `Data inserted successfully` và hiển thị `snapshot_id` (ví dụ: 1). Trong thư mục `data-lake/default/users/data`, bạn sẽ thấy file Parquet được tạo ra.
Thực hiện thao tác UPDATE (Cập nhật dữ liệu)
Iceberg hỗ trợ `update` mà không cần viết lại toàn bộ table. Chúng ta sẽ cập nhật email của user_id = 101. Thao tác này sẽ tạo Snapshot mới (ví dụ snapshot 2) và cập nhật metadata để chỉ trỏ đến file Parquet mới chứa bản ghi đã sửa.
cat > ~/iceberg-lab/update_data.py
Chú ý: Thay `home/ubuntu` bằng đường dẫn home của bạn. Chạy script:
python3 ~/iceberg-lab/update_data.py
Kết quả mong đợi: Thông báo `Data updated successfully` và `snapshot_id` tăng lên (ví dụ: 2). Output pandas hiển thị email của user 101 đã đổi thành `a_new@example.com`.
Verify kết quả cuối cùng
Để đảm bảo kiến trúc hoạt động đúng, hãy kiểm tra số lượng snapshot trong table. Nếu có 2 snapshot (một từ INSERT, một từ UPDATE), chứng tỏ Iceberg đang hoạt động đúng nguyên lý Time Travel và ACID.
cat > ~/iceberg-lab/verify_iceberg.py
Chú ý: Thay `home/ubuntu` bằng đường dẫn home của bạn. Chạy script:
python3 ~/iceberg-lab/verify_iceberg.py
Kết quả mong đợi: Hiển thị 2 snapshot trong lịch sử và danh sách các file Parquet trong thư mục data. Bạn đã hoàn thành việc thiết lập môi trường Iceberg cơ bản và thực hiện các thao tác DML đầu tiên.
Điều hướng series:
Mục lục: Series: Xây dựng Data Lakehouse Serverless với DuckDB, Apache Iceberg và Ubuntu 24.04
« Phần 2: Triển khai DuckDB và thiết lập cơ sở dữ liệu quan hệ trong-memory
Phần 4: Kết nối DuckDB với Apache Iceberg để truy vấn dữ liệu phân tán »