Tối ưu hóa truy vấn với Z-Ordering và Partitioning
Trước khi bắt đầu, cần hiểu rõ sự khác biệt: Partitioning chia nhỏ dữ liệu dựa trên giá trị của cột (ví dụ: theo ngày), trong khi Z-Ordering sắp xếp dữ liệu vật lý dựa trên sự kết hợp của nhiều cột để tối ưu hóa việc loại bỏ dữ liệu (Predicate Pushdown) khi truy vấn.
Để áp dụng Z-Ordering cho bảng Iceberg đã tạo, ta sử dụng lệnh OPTIMIZE trong Spark hoặc Trino. Đây là bước quan trọng nhất để giảm lượng I/O khi query các cột không dùng để partition.
Giả sử bảng sales_data đã được partition theo date, nhưng bạn thường xuyên query theo customer_id và region. Ta sẽ chạy lệnh Optimize trên Spark để tạo Z-Order index.
spark-sql --conf spark.sql.catalog.iceberg=org.apache.iceberg.spark.SparkCatalog \
--conf spark.sql.catalog.iceberg.catalog-impl=org.apache.iceberg.rest.RESTCatalog \
--conf spark.sql.catalog.iceberg.uri=http://trino-server:8080 \
-e "
OPTIMIZE iceberg.sales_data
ZORDER (customer_id, region)
WHERE date >= '2023-01-01' AND date
Kết quả mong đợi: Spark sẽ scan toàn bộ data file trong khoảng thời gian đã định, tạo ra các file mới đã được sắp xếp theo customer_id và region, đồng thời cập nhật metadata manifest của Iceberg.
Cấu hình Partitioning động cho dữ liệu mới
Nếu dữ liệu chưa được partition hoặc cần thay đổi chiến lược, ta dùng ALTER TABLE để thêm partition strategy. Iceberg hỗ trợ nhiều hàm partition như year, month, bucket, truncate.
spark-sql -e "
ALTER TABLE iceberg.sales_data
ADD PARTITIONING (
year(date),
month(date),
bucket(256, customer_id)
);
"
Kết quả mong đợi: Các file mới được ghi vào bảng sẽ tự động tuân theo chiến lược partition mới. Các file cũ vẫn giữ nguyên nhưng truy vấn sẽ tự động loại bỏ chúng nếu không khớp với điều kiện partition.
Verify kết quả tối ưu hóa
Để kiểm tra xem Z-Ordering và Partitioning đã hiệu quả chưa, hãy chạy một query đơn giản và quan sát Explain Plan trong Trino hoặc Spark.
trino --catalog iceberg --schema default -c "
EXPLAIN (VERBOSE)
SELECT * FROM sales_data
WHERE customer_id = 'CUST_12345' AND region = 'US';
"
Kết quả mong đợi: Trong output của Explain Plan, bạn phải thấy dòng Filter xuất hiện ngay sau TableScan, và số lượng file được scan (Files Scanned) giảm đáng kể so với khi chưa tối ưu.
Cấu hình bảo mật và ACL cho Iceberg qua Trino
Trino không lưu trữ dữ liệu, nó chỉ truy vấn. Bảo mật được thực hiện bằng cách cấp quyền truy cập vào catalog và schema của Iceberg thông qua GRANT và REVOKE trong Trino.
Để bật tính năng này, Trino cần được cấu hình để sử dụng System Access Control List (ACL) hoặc Ranger. Ở đây ta dùng ACL mặc định của Trino để đơn giản hóa trên Ubuntu 24.04.
Cấu hình file security.policy trong Trino
Trước tiên, cần khai báo file policy trong cấu hình của Trino Coordinator và Worker. File này định nghĩa ai được làm gì.
Tạo file /etc/trino/etc/security.policy với nội dung hoàn chỉnh dưới đây.
# /etc/trino/etc/security.policy
# Grant SELECT permission on iceberg catalog to user 'analyst'
grant select on catalog iceberg to analyst;
# Grant INSERT, UPDATE, DELETE on schema 'default' to user 'etl_user'
grant insert, update, delete on schema default to etl_user;
# Grant all privileges on table 'sales_data' to role 'data_scientist'
grant all on table default.sales_data to role data_scientist;
# Revoke specific permission if needed
revoke select on table default.sales_data from role guest;
Kết quả mong đợi: File được tạo thành công, không báo lỗi cú pháp.
Bật tính năng Security trong config Trino
Tiếp theo, cập nhật file /etc/trino/etc/catalog/iceberg.properties để Trino nhận diện và áp dụng các chính sách bảo mật.
# /etc/trino/etc/catalog/iceberg.properties
connector.name=iceberg
hive.metastore.uri=thrift://hms-server:9083
hive.metastore-cache-tolerance=1s
hive.metastore-refresh-interval=1s
# Enable ACL enforcement
enforce-hive-storage-format=false
# Nếu dùng LDAP hoặc file-based auth, cần cấu hình thêm ở node.properties
Đồng thời, chỉnh sửa /etc/trino/etc/node.properties để chỉ định file policy vừa tạo.
# /etc/trino/etc/node.properties
node.environment=production
node.id=unique-identifier-1
node.data-dir=/var/trino/data
# Đường dẫn đến file security.policy
security.policy-file=/etc/trino/etc/security.policy
Kết quả mong đợi: Cần khởi động lại dịch vụ Trino (systemctl restart trino) để áp dụng thay đổi.
Verify kết quả bảo mật
Thử đăng nhập bằng user analyst và chạy lệnh SELECT, sau đó thử INSERT để kiểm tra quyền.
# Test user analyst (chỉ được SELECT)
trino --catalog iceberg --schema default --user analyst -c "SELECT * FROM sales_data LIMIT 1;"
# Test user analyst (thử INSERT - phải fail)
trino --catalog iceberg --schema default --user analyst -c "INSERT INTO sales_data VALUES (1, 'test');"
Kết quả mong đợi: Lệnh SELECT chạy thành công, trong khi lệnh INSERT trả về lỗi Permission denied hoặc Access denied.
Tối ưu bộ nhớ JVM cho Spark và Trino trên Ubuntu
Mặc định của JVM trên Ubuntu 24.04 thường không phù hợp với Big Data. Cần điều chỉnh Heap Size và Garbage Collector (GC) để tránh OutOfMemoryError và giảm GC Pause time.
Trên Ubuntu 24.04, Java 21 (LTS) thường được dùng. Ta sẽ chuyển sang ZGC (Z Garbage Collector) vì nó có latency thấp hơn G1GC cho các heap lớn (>4GB).
Tối ưu JVM cho Spark (Executor & Driver)
Tạo file spark-env.sh trong thư mục config của Spark (thường là /opt/spark/conf/spark-env.sh).
# /opt/spark/conf/spark-env.sh
# Export biến môi trường cho Spark
export SPARK_HOME=/opt/spark
export SPARK_CONF_DIR=/opt/spark/conf
# Cấu hình Driver Memory (điều chỉnh theo RAM server, ví dụ 8GB)
export SPARK_DRIVER_MEMORY=8g
export SPARK_DRIVER_MEMORY_OVERHEAD_FACTOR=1.5
# Cấu hình Executor Memory (ví dụ 4GB mỗi executor)
export SPARK_EXECUTOR_MEMORY=4g
export SPARK_EXECUTOR_MEMORY_OVERHEAD_FACTOR=1.5
# Sử dụng ZGC để giảm latency
export SPARK_DRIVER_OPTS="-XX:+UseZGC -XX:MaxGCPauseMillis=50 -Xlog:gc*:file=/var/log/spark/spark-driver-gc.log:time"
export SPARK_EXECUTOR_OPTS="-XX:+UseZGC -XX:MaxGCPauseMillis=50 -Xlog:gc*:file=/var/log/spark/spark-executor-gc.log:time"
# Đảm bảo thư mục log tồn tại
mkdir -p /var/log/spark
Kết quả mong đợi: Khi khởi động Spark, các process sẽ sử dụng ZGC và ghi log GC vào file đã định.
Tối ưu JVM cho Trino (Coordinator & Worker)
Trino sử dụng file config.properties để cấu hình JVM. Chỉnh sửa file /etc/trino/etc/config.properties.
# /etc/trino/etc/config.properties
# Cấu hình JVM Heap Size (ví dụ 16GB cho Coordinator)
trino.jvm.config=-Xms16g -Xmx16g -XX:+UseZGC -XX:MaxGCPauseMillis=50 -Xlog:gc*:file=/var/log/trino/trino-gc.log:time
# Cấu hình cho Worker (nếu khác, có thể dùng node.properties hoặc file riêng)
# Ở đây ta gộp chung vào config.properties cho đơn giản
Ngoài ra, cần tạo file trino.yaml trong thư mục /etc/trino/etc/ để Trino nhận diện cấu hình JVM chi tiết hơn nếu cần.
# /etc/trino/etc/trino.yaml
jvm:
config:
- -Xms16g
- -Xmx16g
- -XX:+UseZGC
- -XX:MaxGCPauseMillis=50
- -Xlog:gc*:file=/var/log/trino/trino-gc.log:time
additional:
- -XX:+UnlockDiagnosticVMOptions
- -XX:ZCollectionInterval=3s
Kết quả mong đợi: Trino khởi động với heap size 16GB và sử dụng ZGC, log GC được ghi vào /var/log/trino/trino-gc.log.
Verify kết quả tối ưu bộ nhớ
Chạy lệnh jstat hoặc ps để kiểm tra heap size thực tế và quan sát log GC.
# Kiểm tra heap size của process Trino
ps -eo pid,ppid,cmd,%mem,%cpu --sort=-%mem | grep trino
# Xem log GC để đảm bảo ZGC đang hoạt động
tail -f /var/log/trino/trino-gc.log
Kết quả mong đợi: Trong log GC, bạn sẽ thấy dòng ZGC thay vì GC hoặc G1, và thời gian pause (Pause Time) dưới 50ms.
Cấu hình Log Monitoring và Alerting cơ bản
Để hệ thống Lakehouse hoạt động ổn định, cần giám sát các log quan trọng của Spark, Trino và Iceberg. Ta sẽ sử dụng Logrotate để quản lý kích thước log và systemd-journald để thu thập log.
Cấu hình Logrotate cho Spark và Trino
Tạo file cấu hình logrotate cho Spark tại /etc/logrotate.d/spark.
# /etc/logrotate.d/spark
/var/log/spark/*.log {
daily
rotate 14
missingok
notifempty
compress
delaycompress
postrotate
# Restart Spark nếu cần, hoặc chỉ thông báo
systemctl reload spark 2>/dev/null || true
endscript
}
Tương tự cho Trino tại /etc/logrotate.d/trino.
# /etc/logrotate.d/trino
/var/log/trino/*.log {
daily
rotate 7
missingok
notifempty
compress
delaycompress
postrotate
systemctl reload trino 2>/dev/null || true
endscript
}
Kết quả mong đợi: Log sẽ được tự động nén và xóa sau 7-14 ngày, tránh đầy disk.
Cấu hình Alerting cơ bản với Fail2Ban hoặc Script
Ở mức cơ bản, ta tạo một script đơn giản để scan log và gửi cảnh báo nếu phát hiện lỗi nghiêm trọng (như OutOfMemoryError hoặc ConnectionRefused).
Tạo file script /usr/local/bin/monitor-lakehouse.sh.
#!/bin/bash
# /usr/local/bin/monitor-lakehouse.sh
LOG_DIR="/var/log"
SPARK_LOG="$LOG_DIR/spark/spark-executor-gc.log"
TRINO_LOG="$LOG_DIR/trino/trino-gc.log"
ALERT_EMAIL="admin@yourdomain.com" # Thay bằng email thật
# Hàm gửi cảnh báo
send_alert() {
local message="$1"
local subject="ALERT: Lakehouse Issue Detected"
echo "$message" | mail -s "$subject" $ALERT_EMAIL
# Hoặc ghi vào file alert.log
echo "$(date): $message" >> /var/log/lakehouse-alert.log
}
# Kiểm tra lỗi OOM trong Spark
if grep -q "OutOfMemoryError" $SPARK_LOG 2>/dev/null; then
send_alert "Spark OOM detected in executor log."
fi
# Kiểm tra lỗi Connection trong Trino
if grep -q "Connection refused\|BindException" $TRINO_LOG 2>/dev/null; then
send_alert "Trino connection error detected."
fi
# Kiểm tra disk usage (nếu > 90%)
DISK_USAGE=$(df /var/log | awk 'NR==2 {print $5}' | sed 's/%//')
if [ "$DISK_USAGE" -gt 90 ]; then
send_alert "Disk usage on /var/log is critical: ${DISK_USAGE}%"
fi
Cấp quyền thực thi cho script.
chmod +x /usr/local/bin/monitor-lakehouse.sh
Cấu hình cron job để chạy script mỗi 5 phút.
crontab -e
# Thêm dòng sau vào file crontab
*/5 * * * * /usr/local/bin/monitor-lakehouse.sh
Kết quả mong đợi: Script chạy định kỳ, nếu có lỗi trong log, email cảnh báo sẽ được gửi hoặc ghi vào file /var/log/lakehouse-alert.log.
Verify kết quả monitoring
Tạo một lỗi giả lập để test script.
# Ghi một dòng OOM giả vào log Spark để test
echo "2024-01-01 12:00:00 java.lang.OutOfMemoryError: Java heap space" >> /var/log/spark/spark-executor-gc.log
# Chạy script thủ công
/usr/local/bin/monitor-lakehouse.sh
# Kiểm tra file alert log
cat /var/log/lakehouse-alert.log
Kết quả mong đợi: File /var/log/lakehouse-alert.log có dòng mới ghi thời gian và nội dung cảnh báo về lỗi OOM vừa tạo.
Điều hướng series:
Mục lục: Series: Xây dựng Data Lakehouse với Apache Iceberg, Trino và Ubuntu 24.04
« Phần 4: Xây dựng quy trình ETL và quản lý schema với Iceberg
Phần 6: Troubleshooting, bài toán thực tế và tips nâng cao »