Sự cố PagerDuty lúc 2 giờ sáng
Quay lại năm 2021, tôi đã trải qua một đêm dài nhìn chằm chằm vào một thảm họa trên môi trường production. Một job Spark đã ghi đè một phần lên một partition S3 quan trọng. Vì các data lake truyền thống coi các thư mục là partition, chúng tôi không có nút “hoàn tác” (undo). Khôi phục từ bản sao lưu lạnh là hy vọng duy nhất. Chúng tôi có nhiều writer cùng truy cập vào một prefix, và nếu không có tính tuân thủ ACID, “nguồn sự thật” (source of truth) của chúng tôi chẳng khác nào một trò chơi may rủi.
Đội ngũ dữ liệu của chúng tôi đã mất 16 giờ mỗi tuần chỉ để sửa các partition bị hỏng một cách thủ công hoặc xử lý các vấn đề về hiệu suất do hội chứng “small file” gây ra. Chúng tôi đã vận hành một Data Lake tiêu chuẩn trên AWS S3 bằng định dạng bảng Hive. Nó hoạt động ổn trong những ngày đầu. Tuy nhiên, khi chúng tôi đạt đến quy mô petabyte, những vết nứt đã trở thành những vực thẳm.
Tại sao Data Lake truyền thống lại thất bại ở quy mô lớn
S3 không phải là vấn đề; vấn đề nằm ở định dạng bảng Hive. Trong thế giới của Hive, một bảng chỉ là một tập hợp các thư mục. Bạn muốn thay đổi kiểu dữ liệu của một cột? Thông thường bạn phải ghi lại toàn bộ tập dữ liệu. Cần xóa một hàng cho yêu cầu GDPR? Bạn phải đọc toàn bộ tệp, lọc nó và ghi lại vào đĩa.
Nợ kỹ thuật (technical debt) thường xuất hiện theo ba cách:
- Partition dựa trên thư mục: Nếu một bản ghi đến muộn với
event_datetừ năm 2019, hệ thống sẽ gặp khó khăn trong việc sắp xếp hoặc tạo ra thêm một thư mục nhỏ lẻ, kém hiệu quả khác. - Không có tính nguyên tử (Atomicity): Một lần ghi thất bại sẽ để lại các tệp “mồ côi” (orphan files). Các truy vấn sau đó sẽ lấy phải dữ liệu rác này, dẫn đến tình trạng sai lệch dữ liệu ngầm.
- Schema cứng nhắc: Việc đổi tên
user_idthànhcustomer_uuidtừng là một cuộc di chuyển dữ liệu kéo dài cả tuần. Hầu hết các đội ngũ đều bỏ cuộc và chấp nhận sống chung với những cái tên cột khó hiểu.
Các ứng cử viên: Hive vs. Delta vs. Iceberg
Ba ứng cử viên đã xuất hiện khi chúng tôi tìm lối thoát. Hive là di sản mà chúng tôi cần loại bỏ. Delta Lake mạnh mẽ nhưng có vẻ quá phụ thuộc vào hệ sinh thái Databricks vào thời điểm đó. Sau đó, chúng tôi phát hiện ra Apache Iceberg.
Được xây dựng ban đầu tại Netflix để xử lý quy mô khổng lồ của họ, Iceberg hoàn toàn bỏ qua cấu trúc thư mục. Thay vào đó, nó theo dõi từng tệp dữ liệu riêng lẻ trong một “manifest file”. Bước chuyển đổi kiến trúc này rất quan trọng. Nó mang lại độ tin cậy của một cơ sở dữ liệu SQL cho bộ lưu trữ S3 rẻ và vô tận.
Một bài học tôi rút ra từ việc di chuyển hàng trăm bảng: dữ liệu nguồn của bạn gần như không bao giờ sạch. Khi tôi cần chuyển nhanh các mẫu CSV sang JSON để kiểm tra schema hoặc mapping, tôi sử dụng toolcraft.app/vi/tools/data/csv-to-json. Nó chạy hoàn toàn trong trình duyệt, nghĩa là các trường dữ liệu nhạy cảm không bao giờ rời khỏi máy tính của bạn.
Xây dựng Lakehouse hiện đại
Nếu bạn bắt đầu từ hôm nay, Spark với Iceberg là tiêu chuẩn vàng. Nó đã trưởng thành và được tài liệu hóa tốt. Đây là cách tôi cấu hình một session để xử lý chiến lược di chuyển.
1. Thiết lập Spark Session
from pyspark.sql import SparkSession
# Trỏ Spark tới Iceberg Catalog
spark = SparkSession.builder \
.appName("IcebergMigration") \
.config("spark.sql.extensions", "org.apache.iceberg.spark.extensions.IcebergSparkSessionExtensions") \
.config("spark.sql.catalog.local", "org.apache.iceberg.spark.SparkCatalog") \
.config("spark.sql.catalog.local.type", "hadoop") \
.config("spark.sql.catalog.local.warehouse", "/tmp/iceberg_warehouse") \
.getOrCreate()
2. Hidden Partitioning: Tính năng “đắt giá”
Iceberg sở hữu tính năng Hidden Partitioning (Partition ẩn). Bạn không cần phải tạo cột day từ timestamp một cách thủ công. Engine sẽ tự xử lý việc chuyển đổi ở bên dưới. Không còn các cột dư thừa nữa.
# Tạo một bảng được partition theo ngày mà không cần thêm cột bổ sung
spark.sql("""
CREATE TABLE local.db.events (
id bigint,
event_time timestamp,
level string,
message string
)
USING iceberg
PARTITIONED BY (days(event_time))
""")
Tiến hóa Schema không còn đau đầu
Trước đây, việc đổi tên một cột đồng nghĩa với một công việc di chuyển dữ liệu kéo dài 12 giờ. Với Iceberg, đó chỉ là một thay đổi về metadata. Nó chỉ mất vài mili giây vì không có tệp dữ liệu nào bị tác động.
-- Thay đổi tức thì, không có thời gian chết (downtime)
ALTER TABLE local.db.events RENAME COLUMN message TO event_details;
ALTER TABLE local.db.events ADD COLUMN correlation_id string;
Lớp metadata này ánh xạ các tệp dữ liệu cũ sang schema mới một cách linh hoạt. Chỉ riêng tính năng này đã giúp đội ngũ của chúng tôi tiết kiệm hàng chục giờ làm việc chân tay trong năm qua.
Time Travel: Siêu năng lực Debug
Hãy tưởng tượng một bên liên quan hỏi: “Tại sao doanh thu ngày hôm qua lại trông khác đi?”. Trong một data lake truyền thống, bạn chỉ có thể đoán. Với Iceberg, bạn có thể truy vấn bảng chính xác như trạng thái của nó lúc 2 giờ chiều thứ Ba tuần trước.
-- Xem lịch sử các snapshot
SELECT * FROM local.db.events.snapshots;
-- Quay ngược thời gian
SELECT * FROM local.db.events FOR TIMESTAMP AS OF (current_timestamp() - INTERVAL 2 DAYS);
Đây thực sự là git revert cho hàng petabyte dữ liệu. Nếu một batch job ghi 50.000 bản ghi bị lỗi, bạn không cần phải đi tìm các tệp đó. Bạn chỉ cần khôi phục lại (roll back) snapshot trước đó.
Con đường thực tế phía trước
Đừng cố gắng di chuyển mọi thứ cùng một lúc. Hãy bắt đầu với bảng “kém tin cậy” nhất — bảng thường xuyên làm hỏng pipeline của bạn nhất. Chạy một quá trình “di chuyển song song” (shadow migration), nơi bạn ghi dữ liệu vào cả Hive và Iceberg trong bảy ngày để xác minh số lượng khớp nhau.
Giữ cho hiệu suất luôn sắc bén với một quy trình bảo trì đơn giản. Iceberg tạo ra nhiều tệp metadata nhỏ, vì vậy bạn cần cắt tỉa chúng định kỳ:
# Xóa các snapshot cũ để thu hồi dung lượng S3
spark.sql("CALL local.system.expire_snapshots('db.events', timestamp '2026-05-20 00:00:00')")
# Gom các tệp nhỏ thành các khối 128MB hiệu quả
spark.sql("CALL local.system.rewrite_data_files('db.events')")
Chuyển sang Iceberg không chỉ là một nâng cấp kỹ thuật; đó là về việc lấy lại những ngày cuối tuần của bạn. Lịch trực on-call của tôi giờ đây yên tĩnh hơn nhiều nhờ các đảm bảo về ACID. Nếu bạn vẫn đang vật lộn với các partition S3 bị hỏng, đã đến lúc chấm dứt tình trạng này.

