FinOps 5 — Chi phí streaming & real-time
Vì sao streaming là một loài chi phí khác
Trong tư duy FinOps (xem FinOps 1 — Tổng quan), một pipeline batch có nhịp chi phí dễ chịu: job chạy lúc 2h sáng, xử lý xong trong 40 phút rồi tắt. Cluster co về 0, bạn chỉ trả tiền cho khoảng thời gian thực chạy. Đây là mô hình "burst rồi ngủ" — đắt trong lúc chạy nhưng phần lớn thời gian không tốn gì.
Hệ thống real-time / streaming thì ngược lại hoàn toàn: nó phải luôn bật, 24/7, để sẵn sàng nhận sự kiện bất cứ lúc nào. Broker phải online cả đêm dù 3h sáng gần như không có giao dịch; job xử lý phải giữ state trong bộ nhớ liên tục; consumer phải poll không nghỉ. Chi phí vì thế không phải là "byte đã xử lý" mà là thời gian tồn tại của hạ tầng — nhân với số 8.760 giờ mỗi năm. Một cluster streaming nhỏ nhưng bật cả năm có thể tốn hơn một job batch nặng chạy vài lần mỗi ngày.
Điều này dẫn tới một sự thật khó chịu về FinOps streaming: real-time thật sự thì đắt, và phần lớn "yêu cầu real-time" trong thực tế không cần đến từng giây. Bài này đi qua ba khối chi phí — (1) message broker (Kafka), (2) stream processing (Flink/Spark Streaming), (3) đánh đổi độ trễ vs chi phí — rồi kết bằng nguyên tắc quan trọng nhất: chỉ trả tiền cho real-time ở đúng chỗ thực sự cần nó.
Chi phí message broker (Kafka)
Kafka (hoặc message broker tương đương) là xương sống của streaming. Nền tảng Kafka được trình bày ở loạt bài Kafka — Kiến trúc; ở đây ta chỉ soi các đòn bẩy chi phí. Hóa đơn broker gồm hai phần chồng lên nhau: compute (số broker và cỡ máy để nhận/phục vụ traffic) và storage (dung lượng đĩa để giữ log). Cả hai đều chịu ảnh hưởng bởi các cấu hình dưới đây.
Retention: giữ log lâu = tốn đĩa
Kafka không xóa message ngay khi consumer đọc xong — nó giữ log trên đĩa theo retention (thời gian hoặc dung lượng). Đây là khoản storage lớn nhất và dễ phình nhất. retention.ms càng dài, đĩa giữ càng nhiều.
Retention tồn tại để cho phép replay — consumer mới hoặc consumer lỗi có thể tua lại từ quá khứ. Câu hỏi FinOps: bạn thực sự cần replay bao xa? Giữ 7 ngày cho một topic mà không ai từng tua lại quá 1 ngày là trả tiền đĩa cho khả năng không dùng đến.
- Topic giao dịch cần replay để tái xử lý khi có bug → giữ vài ngày là hợp lý.
- Topic log/metric ephemeral → vài giờ là đủ.
- Dữ liệu cần lưu lâu dài (nguồn sự thật) → không nên giữ vô hạn trong Kafka (đĩa broker đắt); hãy sink xuống object storage rẻ (data lake) và đặt retention Kafka ngắn.
# Cấu hình MINH HOẠ (không phải SQL chạy được)
retention.ms = 259200000 # 3 ngày thay vì 7 → cắt ~57% storage của topic
cleanup.policy = delete # hoặc "compact" nếu chỉ cần bản mới nhất mỗi key
Log compaction (cleanup.policy=compact) là mẹo tiết kiệm khác: với topic dạng "trạng thái mới nhất theo key" (ví dụ số dư tài khoản hiện tại), compaction chỉ giữ bản ghi cuối của mỗi key thay vì toàn bộ lịch sử — dung lượng gần như không đổi dù có bao nhiêu update.
Replication factor: bền vững đổi bằng chi phí
Mỗi partition được nhân bản replication.factor lần trên các broker khác nhau để chịu lỗi. RF=3 nghĩa là mỗi byte được lưu 3 lần — bền vững cao nhưng storage và traffic nội bộ (replication) nhân 3.
- RF=3: chuẩn cho dữ liệu quan trọng (giao dịch ngân hàng). Không nên giảm.
- RF=2: chấp nhận được cho dữ liệu ít nghiêm trọng, tiết kiệm 1/3 storage nhưng chịu lỗi kém hơn.
- RF=1: chỉ cho dev/test hoặc dữ liệu tái tạo được — mất 1 broker là mất dữ liệu.
Đây là đánh đổi độ bền vs chi phí thuần túy: đừng để RF=3 trên những topic dev/log không cần bền, nhưng tuyệt đối giữ RF=3 cho topic nghiệp vụ lõi.
Số partition: quá nhiều tốn tài nguyên
Partition cho phép song song hóa, nhưng mỗi partition có chi phí cố định: file handle, bộ nhớ metadata trên broker và controller, thread replication, và thời gian recovery/rebalance khi broker khởi động. Một cluster với hàng trăm nghìn partition (thường do đặt số partition quá cao "cho chắc" trên mỗi topic) sẽ ngốn RAM broker, kéo dài thời gian failover và làm rebalance chậm — buộc phải nâng cấp máy broker đắt hơn dù throughput thực chẳng cao.
Nguyên tắc: chọn số partition theo throughput thực tế cần và số consumer song song, không phải theo cảm tính. Công thức thô: partition ≈ max(throughput mong muốn / throughput mỗi partition, số consumer song song). Tăng partition dễ nhưng giảm rất khó (Kafka không cho giảm) — nên đừng over-provision từ đầu.
Nén message
Producer có thể nén batch message trước khi gửi (compression.type). Nén giảm đồng thời storage đĩa, băng thông network (kể cả traffic replication) và egress cross-AZ — một đòn bẩy đánh vào nhiều mặt chi phí cùng lúc.
| Codec | Tỷ lệ nén | CPU | Dùng khi |
|---|---|---|---|
| none | — | 0 | Hầu như không nên |
| lz4 | Thấp–TB | Rất thấp | Mặc định cân bằng tốt |
| snappy | Trung bình | Thấp | Phổ biến, ổn định |
| zstd | Cao | Vừa | Tiết kiệm storage/network tối đa |
Với message JSON (rất phổ biến và nén cực tốt), bật zstd hoặc lz4 thường cắt 60–80% dung lượng. CPU tăng thêm ở producer/broker thường rẻ hơn nhiều so với đĩa và băng thông tiết kiệm được.
Tiered storage: đẩy log cũ xuống object storage rẻ
Kafka hiện đại hỗ trợ tiered storage: log gần đây (hot) nằm trên đĩa broker nhanh và đắt, còn log cũ được offload xuống object storage (S3/GCS) rẻ hơn nhiều bậc — nhưng vẫn truy cập được qua chính API Kafka khi cần replay. Điều này tách rời retention khỏi chi phí đĩa broker: bạn có thể giữ retention dài (thậm chí nhiều tháng cho replay) mà không phải trả tiền đĩa broker đắt cho toàn bộ.
Cơ chế này lặp lại đúng ý tưởng phân tầng lưu trữ ở FinOps 3 — Chi phí lưu trữ & phân tầng: dữ liệu càng cũ, càng ít đọc → đẩy xuống tầng rẻ.
Managed (MSK/Confluent) vs self-host
- Self-host (tự vận hành trên VM/K8s): giá hạ tầng thuần thấp nhất, nhưng "chi phí ẩn" là công sức vận hành — vá lỗi, cân bằng partition, xử lý sự cố broker lúc 3h sáng, nâng cấp phiên bản. Với đội nhỏ, chi phí nhân sự này thường vượt phần tiết kiệm hạ tầng.
- Managed (Amazon MSK, Confluent Cloud): trả premium cho nhà cung cấp lo vận hành, HA, patching. Đắt hơn trên hóa đơn cloud nhưng thường rẻ hơn khi tính tổng chi phí sở hữu (TCO) với đội thiếu người. Confluent Cloud kiểu serverless còn tính theo throughput/storage thực — hợp workload biến động.
Không có đáp án đúng tuyệt đối: ngân hàng có đội platform mạnh và yêu cầu chủ quyền dữ liệu có thể chọn self-host; đội nhỏ ưu tiên tốc độ nên chọn managed. Điểm mấu chốt FinOps là tính cả chi phí người, đừng chỉ nhìn giá VM.
Chi phí stream processing (Flink / Spark Streaming)
Broker chỉ vận chuyển message; stream processing engine mới là nơi tính toán liên tục — join, aggregate, phát hiện mẫu. Xem chi tiết ở Apache Flink — Tổng quan và Spark Structured Streaming (loạt Spark). Vì job chạy 24/7, cách cấu hình quyết định trực tiếp hóa đơn.
Parallelism đúng mức, tránh over-provision slot
Job stream chạy trên các slot / task song song. Mỗi slot chiếm CPU và RAM liên tục, kể cả lúc rảnh. Đặt parallelism cao "cho chắc" nghĩa là trả tiền cho slot ngồi không 24/7.
Nguyên tắc right-size: đo throughput thực và lag (độ trễ consumer so với đầu topic). Nếu lag ổn định gần 0 với parallelism thấp hơn, giảm slot. Nếu lag tăng dần (job không theo kịp), tăng slot. Mục tiêu là parallelism vừa đủ giữ lag thấp, không hơn. Over-provision slot là dạng lãng phí phổ biến nhất trong streaming vì nó "im lặng" — không có cảnh báo nào bảo bạn đang thừa.
State store lớn tốn kém
Job stateful (window aggregation, join theo thời gian, dedup) phải giữ state — thường trong bộ nhớ hoặc backend như RocksDB. State lớn nghĩa là cần RAM/đĩa lớn hơn trên mỗi task, checkpoint nặng hơn, recovery lâu hơn. State phình không kiểm soát là nguồn chi phí âm thầm.
Cách kiềm chế state:
- Đặt TTL cho state (ví dụ dedup key hết hạn sau 24h) để nó không tích lũy vô hạn.
- Chọn cửa sổ (window) ngắn nhất mà nghiệp vụ chấp nhận — window 1 giờ giữ state gấp 60 lần window 1 phút.
- Dùng RocksDB state backend (spill xuống đĩa) cho state lớn thay vì ép hết vào heap RAM đắt.
Checkpoint interval
Checkpoint là ảnh chụp state định kỳ để phục hồi khi lỗi (xem Flink — Fault tolerance). Checkpoint quá dày (ví dụ mỗi vài giây) tạo tải I/O và network lớn — ghi state xuống storage bền liên tục, tốn cả CPU lẫn phí ghi object storage. Quá thưa thì recovery lâu và mất nhiều tiến độ khi lỗi.
Cân bằng: chọn interval theo mức mất mát chấp nhận được khi restart (thường 30 giây–vài phút cho phần lớn workload), không phải nhỏ nhất có thể. Với state lớn, checkpoint dày còn có thể làm job không kịp hoàn tất checkpoint trước cái tiếp theo.
Autoscaling theo tải
Traffic streaming thường có nhịp: giờ hành chính đông giao dịch, đêm khuya thưa. Cấu hình cứng cho peak nghĩa là trả tiền peak suốt 24h. Autoscaling cho phép engine tự tăng/giảm slot theo lag và throughput — co lại lúc thưa, giãn ra lúc đông. Flink có autoscaler (reactive/adaptive scheduler), Spark có dynamic allocation cho streaming.
Autoscaling là đòn bẩy mạnh nhất để cắt chi phí "thời gian tồn tại": thay vì cấp cứng cho peak, chỉ trả cho tài nguyên thực dùng theo từng thời điểm. Điều kiện là workload phải chịu được co giãn (state không quá khổng lồ để rebalance nhanh).
Đánh đổi độ trễ vs chi phí
Đây là trục quyết định quan trọng nhất. Độ trễ càng thấp thì chi phí càng cao — và mối quan hệ này phi tuyến: đi từ "vài phút" xuống "dưới 1 giây" đắt hơn nhiều so với từ "hằng giờ" xuống "vài phút".
| Chế độ | Độ trễ điển hình | Chi phí tương đối | Phù hợp |
|---|---|---|---|
| Batch | Giờ – ngày | Thấp nhất | Báo cáo cuối ngày, tổng hợp kế toán |
| Micro-batch | Phút | Trung bình | Dashboard cập nhật vài phút/lần |
| Near-real-time | Vài giây | Cao | Cảnh báo, giám sát |
| Real-time thật | Mili-giây | Cao nhất | Chặn giao dịch gian lận tức thời |
Câu hỏi FinOps cốt lõi trước mọi dự án streaming: nghiệp vụ có thực sự cần từng giây không, hay micro-batch/near-real-time là đủ? Rất nhiều "yêu cầu real-time" khi hỏi kỹ hóa ra chỉ là "muốn dữ liệu tươi hơn báo cáo cuối ngày". Một dashboard quản lý cập nhật mỗi 5 phút bằng micro-batch không cần một cluster Flink real-time chạy 24/7 với độ trễ mili-giây — chênh lệch chi phí có thể gấp nhiều lần cho một khác biệt người dùng không cảm nhận được.
Nguyên tắc: hạ độ trễ có chi phí, nên chỉ mua độ trễ mà nghiệp vụ thực sự dùng. Micro-batch (như Spark Structured Streaming với trigger vài phút) thường là điểm ngọt: tươi hơn batch nhiều, rẻ hơn real-time thật nhiều, và co về 0 giữa các trigger.
Giám sát, right-size và egress
Ba việc vận hành giữ chi phí streaming không trôi:
- Right-size theo throughput thực: định kỳ đối chiếu tài nguyên cấp phát với throughput và lag thực đo được. Streaming dễ over-provision vì "sợ tụt lag" — nhưng slot thừa chạy 24/7 rất tốn.
- Giám sát lag & tài nguyên: consumer lag là chỉ số sức khỏe và chi phí. Lag ổn định gần 0 với ít tài nguyên = right-size tốt. Lag tăng = thiếu tài nguyên (hoặc bug). Kết hợp với dashboard chi phí — xem FinOps 6 — Giám sát & phát hiện bất thường.
- Egress giữa vùng (region/AZ): traffic streaming rất lớn và liên tục, nên phí cross-AZ/cross-region cực dễ tích lũy. Đặt broker và stream processor cùng AZ/region, giữ producer/consumer gần cluster. Một cluster Kafka trải 3 AZ cho HA sẽ có traffic replication cross-AZ tính phí — cân nhắc khi thiết kế. Nguyên tắc egress giống FinOps 3.
Use case thực tế
Bối cảnh: NCB xây pipeline giám sát giao dịch real-time để phát hiện gian lận. Đội ban đầu dựng: cluster Kafka self-host 3 broker (RF=3), topic transactions retention 7 ngày, mỗi topic 100 partition "cho chắc"; job Flink stateful với parallelism 48, checkpoint mỗi 5 giây, chạy 24/7 cấu hình cứng cho peak. Đồng thời đội BI cũng đòi "dashboard real-time" số dư và doanh số chi nhánh.
Chẩn đoán:
- Kafka: retention 7 ngày trong khi chưa ai replay quá 1 ngày; 100 partition/topic gây RAM broker cao và rebalance chậm dù throughput chỉ ~15k msg/s; message JSON chưa nén.
- Flink: parallelism 48 nhưng lag luôn ~0 kể cả giờ cao điểm (thừa slot); checkpoint 5 giây tạo tải I/O liên tục; cấu hình cứng cho peak nên đêm khuya vẫn trả full.
- "Dashboard real-time" của BI thực chất chỉ cần cập nhật mỗi 5 phút.
Hành động:
- Retention
transactions7 → 2 ngày; bật tiered storage đẩy log cũ xuống S3 (giữ khả năng replay dài mà không tốn đĩa broker). - Giảm partition xuống mức hợp throughput (đặt topic mới ~24 partition); bật nén zstd ở producer → dung lượng và traffic replication giảm ~70%.
- Flink: giảm parallelism 48 → 16 (lag vẫn ~0), đặt state TTL 24h cho dedup key, nới checkpoint 5s → 60s, bật autoscaler để đêm co lại.
- Tách "dashboard real-time" của BI sang micro-batch 5 phút (Spark Structured Streaming), không đụng cluster fraud real-time. Giữ Flink real-time chỉ cho phát hiện gian lận — use case thực sự cần mili-giây.
Kết quả (minh hoạ): chi phí cụm streaming giảm khoảng ~55–60% so với ban đầu, chủ yếu nhờ giảm slot Flink, giảm đĩa broker (retention + tiered + nén) và loại bỏ một cluster real-time thừa cho BI. Quan trọng hơn: năng lực phát hiện gian lận không đổi vì phần thực sự cần real-time được giữ nguyên, chỉ cắt phần over-provision và phần không cần real-time.
Ghi nhớ
- Streaming trả tiền theo thời gian tồn tại (24/7), không phải byte xử lý như batch — đây là khác biệt chi phí căn bản.
- Kafka storage: retention càng dài càng tốn đĩa — cắt về mức replay thực cần; dùng compaction cho topic "trạng thái mới nhất theo key".
- Replication factor là đánh đổi bền vs chi phí: giữ RF=3 cho topic nghiệp vụ lõi, hạ RF cho dev/log.
- Số partition có chi phí cố định (RAM, rebalance, recovery) — chọn theo throughput thực, đừng over-provision (giảm partition rất khó).
- Bật nén (zstd/lz4) cắt đồng thời đĩa, network và egress — đặc biệt hiệu quả với JSON.
- Tiered storage đẩy log cũ xuống object storage rẻ, tách retention khỏi chi phí đĩa broker.
- Managed vs self-host: tính cả chi phí người vận hành (TCO), không chỉ giá VM.
- Processing: right-size parallelism theo lag thực (tránh slot thừa 24/7), đặt TTL/window để state không phình, chọn checkpoint interval cân bằng, bật autoscaling theo nhịp tải.
- Độ trễ càng thấp càng đắt (phi tuyến) — luôn hỏi: có cần từng giây không hay micro-batch/near-real-time là đủ?
- Chỉ dùng real-time cho use case thực sự cần (chống gian lận, cảnh báo tức thời); phần còn lại để batch/micro-batch rẻ hơn nhiều.
- Giám sát lag & tài nguyên làm chỉ số kép sức khỏe + chi phí; đặt broker và processor cùng AZ/region để tránh egress.
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.