FinOps 3 — Chi phí lưu trữ & phân tầng
Vì sao lưu trữ tưởng rẻ mà lại tốn
Trong tư duy FinOps (xem FinOps 1 — Tổng quan), người ta hay nghĩ compute mới là khoản đắt còn lưu trữ thì "gần như miễn phí". Điều này đúng ở quy mô nhỏ, nhưng với một ngân hàng phải giữ lịch sử giao dịch nhiều năm theo luật thì lưu trữ trở thành một dòng chi phí âm ỉ, tăng đều mỗi tháng và rất khó thấy. Object storage như Amazon S3 tính tiền theo GB-tháng: bạn không xóa gì, dung lượng chỉ có tăng, và một ngày kia hóa đơn lưu trữ đã bằng nửa hóa đơn compute.
Điểm mấu chốt: cách bạn để dữ liệu — ở tầng nào, định dạng gì, nén ra sao, phân vùng thế nào — quyết định đồng thời cả chi phí lưu trữ lẫn chi phí quét (scan) khi truy vấn. Một file CSV không nén 1 TB có thể co xuống 150 GB dạng Parquet nén ZSTD, và khi engine chỉ đọc 3 cột thay vì cả dòng, chi phí compute cũng giảm theo. Vì thế bài này coi storage là bài toán kép: giảm byte lưu + giảm byte đọc, qua sáu đòn bẩy: (1) tầng lưu trữ theo tần suất truy cập, (2) định dạng và nén, (3) phân vùng và bố cục file, (4) vòng đời và retention, (5) dọn dữ liệu trùng/bỏ quên, (6) egress và bảo trì table format.
Các tầng lưu trữ theo tần suất truy cập
Nhà cung cấp cloud phân dữ liệu object thành nhiều storage class (tầng lưu trữ), rẻ dần theo mức độ "lạnh" — tức càng ít truy cập thì lưu càng rẻ nhưng lấy ra càng chậm và đắt. Đây là đánh đổi trung tâm của toàn bộ chiến lược tiering.
| Tầng | Ví dụ (S3) | Đặc điểm | Giá lưu | Truy xuất |
|---|---|---|---|---|
| Hot / Standard | S3 Standard | Truy cập thường xuyên, độ trễ ms | Cao nhất | Ngay, rẻ |
| Warm / Infrequent | S3 Standard-IA, One Zone-IA | Ít truy cập nhưng cần lấy nhanh | ~50% Standard | Có phí retrieval theo GB |
| Cold | S3 Glacier Instant/Flexible | Lưu lâu, ít khi đọc | Rất thấp | Chậm (phút–giờ) + phí |
| Archive | S3 Glacier Deep Archive | Lưu trữ tuân thủ, gần như không đọc | Thấp nhất | 12–48h + phí cao |
Ngoài giá GB-tháng, có ba loại phí ẩn hay bị bỏ qua:
- Phí truy xuất (retrieval): tầng lạnh tính tiền mỗi GB lấy ra. Nếu bạn đẩy dữ liệu xuống Glacier rồi lại truy vấn thường xuyên, tổng chi phí có thể đắt hơn để ở Standard.
- Thời gian lưu tối thiểu (minimum storage duration): IA thường buộc lưu tối thiểu 30 ngày, Glacier 90 ngày, Deep Archive 180 ngày. Xóa sớm vẫn bị tính đủ thời gian tối thiểu.
- Phí per-object (overhead): mỗi object ở IA/Glacier có phụ phí metadata cố định (ví dụ 8–40 KB/object bị tính như dữ liệu). Đây là lý do small files rất độc — xem phần phân vùng.
Nguyên tắc: chỉ đẩy xuống tầng lạnh những dữ liệu thật sự ít đọc và đủ lớn. Đừng archive một triệu file 5 KB — phí overhead và retrieval sẽ ăn hết phần tiết kiệm.
Object storage vs block storage
Cần phân biệt hai loại lưu trữ nền tảng vì chúng có mô hình giá khác hẳn:
- Block storage (EBS, Persistent Disk): là "ổ đĩa ảo" gắn vào máy chủ. Tính tiền theo dung lượng cấp phát (provisioned), không phải dung lượng dùng — cấp 500 GB mà dùng 50 GB vẫn trả 500 GB. Đắt, chỉ dùng cho hệ điều hành, database vận hành, thư mục làm việc của compute.
- Object storage (S3, GCS): tính theo dung lượng thực dùng, rẻ hơn nhiều bậc, có tiering, lifecycle. Đây là nơi để data lake, backup, lịch sử giao dịch.
Sai lầm tốn kém thường gặp: để dữ liệu lịch sử phình to trên block storage của một máy Spark/database thay vì đẩy sang object storage phân tầng. Với data lake ngân hàng, gần như toàn bộ dữ liệu phân tích nên nằm trên object storage.
Định dạng và nén: giảm byte lưu, giảm byte đọc
Đây là đòn bẩy có ROI cao nhất và làm được một lần cho tất cả.
Cột vs dòng
- Định dạng dòng (row-based): CSV, JSON — mỗi bản ghi là một dòng, tất cả cột nằm liền nhau. Muốn đọc 1 cột vẫn phải quét cả dòng. Nén kém vì các giá trị khác kiểu nằm cạnh nhau.
- Định dạng cột (columnar): Parquet, ORC — dữ liệu nhóm theo cột. Cùng một cột chứa giá trị đồng nhất (ví dụ cả triệu số tiền
amount) nên nén rất tốt. Engine chỉ đọc đúng cột cần (column pruning) và bỏ qua block không khớp filter (predicate pushdown nhờ min/max thống kê trong footer).
Kết quả kép: Parquet giảm dung lượng lưu 3–8 lần so với CSV, đồng thời một truy vấn SELECT amount FROM transactions WHERE ... chỉ quét đúng cột amount thay vì toàn bộ dòng — giảm cả chi phí scan (BigQuery, Athena, Snowflake đều tính tiền theo byte quét). Với data lake ngân hàng, mặc định luôn là Parquet/ORC; CSV/JSON chỉ nên tồn tại ở tầng landing thô rồi convert ngay. Chi tiết định dạng lưu trữ xem FinOps 2 — Chi phí Data Warehouse và bài Data Lake, Warehouse & Lakehouse.
Chọn codec nén
Trong Parquet/ORC bạn còn chọn codec nén cho từng file, đánh đổi giữa tỷ lệ nén và CPU:
| Codec | Tỷ lệ nén | Tốc độ (CPU) | Dùng khi |
|---|---|---|---|
| Snappy | Trung bình | Rất nhanh | Mặc định cho dữ liệu hot, đọc/ghi nhiều |
| ZSTD | Cao (chỉnh level được) | Nhanh vừa | Cân bằng tốt nhất, ngày càng phổ biến |
| gzip | Cao | Chậm | Dữ liệu cold/archive, ưu tiên tối thiểu byte |
| LZ4 | Thấp–TB | Cực nhanh | Ưu tiên tốc độ tuyệt đối |
Quy tắc thực dụng: dữ liệu nóng (đọc nhiều) → Snappy hoặc ZSTD level thấp để tiết kiệm CPU khi giải nén; dữ liệu lạnh (archive) → ZSTD level cao hoặc gzip để nén tối đa vì hầu như không đọc. ZSTD đang dần trở thành lựa chọn mặc định vì nén tốt gần bằng gzip mà nhanh gần bằng Snappy. Lưu ý: nén nhiều hơn nghĩa là byte quét ít hơn → nếu engine tính tiền theo byte quét thì codec tốt tiết kiệm cả hai phía.
Phân vùng và bố cục file
Cách sắp xếp file trên lake ảnh hưởng trực tiếp đến chi phí quét.
Phân vùng (partitioning): chia dữ liệu thành thư mục theo cột lọc phổ biến, điển hình là ngày — ví dụ transactions/year=2026/month=06/day=15/. Khi truy vấn có WHERE created_at >= ..., engine chỉ đọc đúng thư mục liên quan (partition pruning), bỏ qua phần còn lại. Với ngân hàng, phân vùng theo ngày (hoặc năm/tháng) là chuẩn vì hầu hết truy vấn đều có điều kiện thời gian.
Nhưng phân vùng phải cân bằng, và cả hai thái cực đều tốn tiền:
- Small files problem: phân vùng quá mịn (ví dụ theo giờ + theo chi nhánh) tạo ra hàng triệu file nhỏ vài KB. Mỗi file có overhead metadata, mỗi lần đọc là một request (S3 tính phí GET), và engine tốn thời gian mở/đóng file thay vì đọc dữ liệu. Streaming ingest hay sinh vấn đề này.
- File quá lớn: file vài GB làm giảm song song hóa, một task phải đọc cả khối, khó pruning trong file. Cỡ file "vàng" thường là 128 MB – 1 GB/file.
Nguyên tắc bố cục: nhắm cỡ file trong khoảng 128 MB–1 GB, chỉ phân vùng theo cột có lực chọn (cardinality) vừa phải và hay dùng để lọc. Đừng phân vùng theo cột cardinality cao như account_id (sinh vô số thư mục nhỏ).
Minh hoạ phân loại dữ liệu theo tuổi để tiering
Trước khi thiết kế lifecycle, ta cần nhìn phân bố dữ liệu theo thời gian để biết đâu là "nóng" nên giữ gần, đâu là "cũ" nên đẩy archive. Ví dụ đếm và gộp giao dịch theo năm trên sandbox PostgreSQL:
-- ▶ Chạy được
SELECT
EXTRACT(YEAR FROM created_at) AS nam,
COUNT(*) AS so_giao_dich,
SUM(amount) AS tong_tien
FROM transactions
GROUP BY EXTRACT(YEAR FROM created_at)
ORDER BY nam;
Query này cho bức tranh "khối lượng theo năm": năm gần đây thường chiếm phần lớn truy vấn (giữ ở tầng hot/Standard), còn các năm xa có thể an toàn đẩy xuống cold/archive. Cùng cách đó, bạn có thể lọc dữ liệu cũ hơn N năm để lên kế hoạch chuyển tier:
-- ▶ Chạy được
SELECT COUNT(*) AS ban_ghi_cu, MIN(created_at) AS cu_nhat
FROM transactions
WHERE created_at < NOW() - INTERVAL '5 years';
Trên data lake, tiêu chí "tuổi" này chính là thứ mà lifecycle policy dùng để tự động chuyển tier hoặc xóa.
Vòng đời dữ liệu và retention
Không ai đi tay chuyển từng file xuống Glacier. Ta khai báo lifecycle policy — luật tự động chuyển tier hoặc xóa object theo tuổi. Ví dụ chính sách cho lịch sử giao dịch:
- 0–90 ngày: S3 Standard (nóng, dashboard và tra cứu hằng ngày).
- 90–365 ngày: chuyển Standard-IA (warm).
- 1–3 năm: chuyển Glacier Flexible (cold).
- 3–10 năm: chuyển Glacier Deep Archive (tuân thủ luật lưu trữ).
- Sau thời hạn luật định: xóa (expiration).
Điểm quan trọng: retention là quyết định governance, không phải kỹ thuật thuần. Thời hạn giữ dữ liệu phải bám luật (ngân hàng VN thường phải lưu chứng từ giao dịch nhiều năm) và chính sách nội bộ, nên chính sách lifecycle cần khớp với ma trận retention của bộ phận tuân thủ — liên hệ chặt với Kiểm soát truy cập & governance. Đẩy dữ liệu xuống archive sớm hơn nhu cầu đọc thực tế giúp tiết kiệm; nhưng xóa sớm hơn hạn luật định là rủi ro pháp lý — hai chiều này phải được cân bằng bằng policy rõ ràng, có phê duyệt.
Dọn dữ liệu trùng và bỏ quên
Phần lớn "mỡ thừa" trong hóa đơn lưu trữ không phải dữ liệu đang dùng, mà là những thứ bị bỏ quên:
- Orphan data: file còn trên lake nhưng không còn bảng/pipeline nào tham chiếu (job cũ ghi ra rồi bỏ, thư mục staging không dọn). Không ai đọc nhưng vẫn tính tiền hằng tháng.
- Snapshot / backup cũ: bản chụp database, ảnh EBS, backup tự động tích tụ theo năm. Cần policy giữ N bản gần nhất, xoay vòng phần còn lại.
- Bảng tạm / dev / sandbox: bảng
tmp_,_bak, kết quả thử nghiệm còn sót trong warehouse và lake. - Time Travel & fail-safe: các warehouse/table format giữ lịch sử phiên bản để "quay ngược thời gian". Rất tiện nhưng tốn tiền lưu trữ vì mọi phiên bản cũ đều được giữ. Snowflake có Time Travel (0–90 ngày tùy edition) cộng thêm 7 ngày fail-safe không tắt được; giữ Time Travel dài trên bảng lớn thay đổi liên tục có thể nhân đôi chi phí lưu. Chi tiết xem Snowflake — Quản lý chi phí.
Cách kiểm soát: đặt lịch quét orphan (đối chiếu danh mục file thực với metadata/catalog), gắn TTL cho bảng tạm, rút ngắn Time Travel về mức nghiệp vụ thực cần (thường 1–7 ngày là đủ, không cần 90). Một đợt "dọn nhà" định kỳ hằng quý thường cắt được 10–30% dung lượng lake.
Nén, vacuum và table format hiện đại
Với open table format như Delta Lake và Apache Iceberg (lớp giao dịch ACID trên Parquet), việc bảo trì bố cục file được chuẩn hóa thành các lệnh:
- OPTIMIZE / compaction: gộp nhiều small files thành file cỡ tối ưu, giảm số file và chi phí quét. Delta có
OPTIMIZE, kèm Z-Order để sắp lại dữ liệu theo cột lọc thường dùng (data skipping tốt hơn). - VACUUM: xóa file dữ liệu cũ không còn được phiên bản hiện tại tham chiếu (sau khi rewrite/compaction). Đây chính là bước thu hồi dung lượng — nhưng nhớ: VACUUM giới hạn khả năng Time Travel về quá khứ, nên đặt retention (ví dụ 7 ngày) cân bằng giữa an toàn rollback và tiết kiệm.
- Expire snapshots (Iceberg): dọn snapshot cũ và manifest, giải phóng file orphan.
Lịch bảo trì điển hình: OPTIMIZE hằng ngày trên bảng ingest liên tục, VACUUM hằng tuần với retention 7 ngày. Tự động hóa các bước này trong pipeline (xem FinOps 4 — Chi phí compute pipeline) để không phải nhớ thủ công.
Chi phí egress và data transfer
Một khoản dễ gây sốc trên hóa đơn: egress — phí chuyển dữ liệu ra ngoài cloud hoặc giữa các vùng (region). Nạp dữ liệu vào cloud thường miễn phí, nhưng lấy ra Internet, sao chép cross-region, hay cross-AZ đều tính tiền theo GB và thường khá đắt.
Các mẫu tốn egress hay gặp:
- Compute ở region A đọc data lake ở region B (mỗi lần quét là một lần transfer).
- Copy dữ liệu ra hệ thống on-prem hoặc đối tác qua Internet thay vì qua kết nối riêng.
- Cross-AZ traffic giữa các node Spark/Kafka đặt ở AZ khác nhau.
Nguyên tắc thiết kế giảm egress: đặt compute cùng region với storage; giữ dữ liệu và job trong cùng AZ khi có thể; dùng nén (ít byte hơn thì egress rẻ hơn); dùng VPC endpoint / PrivateLink để traffic tới object storage không qua Internet gateway; và với chia sẻ dữ liệu, ưu tiên cơ chế share không sao chép (data sharing) thay vì export ra ngoài. Với ngân hàng, dữ liệu và compute nên gom về một region gần Việt Nam để vừa giảm egress vừa đáp ứng yêu cầu chủ quyền dữ liệu.
Use case thực tế
Bối cảnh: Data lake của NCB lưu bảng transactions lịch sử 10 năm ở dạng CSV gzip trên S3 Standard, phân vùng theo ngày nhưng streaming ingest sinh ra small files. Dung lượng 40 TB, hóa đơn lưu trữ ~920 USD/tháng (chưa kể chi phí scan cao vì CSV).
Chẩn đoán:
- CSV không cho column pruning → mọi truy vấn quét full dòng.
- 12 triệu file, phần lớn < 5 MB (small files) → overhead lớn, GET request nhiều.
- Toàn bộ để Standard dù 8 năm dữ liệu cũ gần như không ai đọc.
- Time Travel / snapshot cũ chưa dọn.
Hành động:
- Convert CSV → Parquet + ZSTD: 40 TB co còn ~9 TB (giảm ~4,4 lần dung lượng, và scan chỉ đọc cột cần).
- Chuyển sang Delta, chạy
OPTIMIZEgộp 12 triệu file → ~40.000 file cỡ ~200 MB.VACUUMretention 7 ngày dọn file cũ. - Áp lifecycle: 0–1 năm Standard, 1–3 năm IA, 3–10 năm Glacier Deep Archive. Kết quả ~1 TB ở Standard, ~2 TB IA, ~6 TB Deep Archive.
- Đặt compute Spark cùng region với S3, dùng VPC endpoint → egress cross-region về 0.
Kết quả (minh hoạ, đơn giá gần đúng): lưu trữ từ ~920 USD/tháng còn khoảng ~150 USD/tháng (Standard ~23, IA ~25, Deep Archive ~6/TB) — giảm ~84%. Đồng thời chi phí quét giảm mạnh nhờ Parquet + partition/data skipping. Đổi lại: dữ liệu > 3 năm khi cần tra cứu (yêu cầu kiểm toán, tranh chấp) phải chờ restore từ Deep Archive 12–48h — chấp nhận được vì tần suất rất thấp, và được ghi rõ trong SLA nghiệp vụ.
Ghi nhớ
- Lưu trữ là bài toán kép: tối ưu để giảm cả byte lưu lẫn byte đọc (scan) — chúng đi cùng nhau.
- Tiering theo tần suất truy cập: hot/Standard → warm/IA → cold/Glacier → archive/Deep Archive; rẻ dần nhưng có phí retrieval, thời gian lưu tối thiểu và overhead per-object.
- Chỉ archive dữ liệu thật sự ít đọc và đủ lớn; đừng đẩy triệu small file xuống tầng lạnh.
- Parquet/ORC (cột) thắng CSV/JSON (dòng) cả về nén lẫn column/predicate pushdown; chọn codec theo nhiệt độ dữ liệu: Snappy/ZSTD cho hot, gzip/ZSTD level cao cho cold.
- Cỡ file vàng 128 MB–1 GB; tránh cả small files lẫn file quá lớn; phân vùng theo cột lọc phổ biến (thường là thời gian), không phân vùng theo cột cardinality cao.
- Lifecycle policy tự chuyển tier/xóa theo tuổi; retention bám luật + governance, không tự ý xóa sớm hơn hạn luật định.
- Dọn orphan data, snapshot/backup cũ, bảng tạm; rút ngắn Time Travel/fail-safe về mức nghiệp vụ thực cần — chúng ngốn dung lượng âm thầm.
- Với Delta/Iceberg:
OPTIMIZE/compaction +VACUUM/expire snapshots định kỳ để gộp file và thu hồi dung lượng. - Egress (ra Internet / cross-region / cross-AZ) đắt: đặt compute cùng region với storage, dùng VPC endpoint và data sharing không sao chép.
Bài viết liên quan
CSV/JSON/Parquet/Avro, lưu trữ theo hàng vs cột, nén, và OLTP vs OLAP.
Data Engineering là gì, vai trò trong vòng đời dữ liệu, và bức tranh hệ sinh thái công cụ.
Vì sao xử lý phân tán, mô hình Spark (RDD/DataFrame), lazy evaluation, shuffle và tối ưu.
Đảm bảo chất lượng & minh bạch dữ liệu bằng dbt: data tests dựng sẵn (unique, not_null, relationships, accepted_values), singular test, unit test, và tài liệu tự sinh kèm lineage graph.