Suốt mười một bài, chúng ta mổ xẻ Kafka từ bên trong: nó là một append-only log (không phải queue), partition cho song song, consumer group chia việc, delivery semantics, offset, lag, tuning, retention, replication, serialization, outbox. Mỗi bài kèm một phép đo thật trong lab — từ 527 nghìn message/giây trên một node tới Avro nhỏ hơn JSON 2,4 lần. Nhưng kiến thức rời rạc không giúp bạn ra quyết định. Câu hỏi thực tế mà một kỹ sư backend phải trả lời là: dự án của tôi có nên dùng Kafka không, và nếu có thì cấu hình thế nào cho đúng? Bài cuối này (phần 12/12) gộp mọi thứ thành hai công cụ ra quyết định: một so sánh để chọn công cụ, và một checklist để vận hành.

Khi nào dùng Kafka — và khi nào đừng

Sai lầm phổ biến nhất về Kafka không phải cấu hình sai — mà là dùng khi không cần. Kafka mạnh, nhưng là một hệ phân tán có trạng thái, tốn chi phí vận hành thật (cluster, giám sát, cân partition). Với nhiều bài toán, một hàng đợi đơn giản hơn là lựa chọn đúng.

Ảnh chụp bảng so sánh nền tối khi nào dùng Kafka và khi nào không Kafka mạnh nhưng không miễn phí về vận hành so với RabbitMQ và hàng đợi trên database để chọn đúng công cụ. Kafka vs RabbitMQ vs hàng đợi trên database tiêu chí mô hình Kafka append-only log phát lại RabbitMQ message queue xoá sau khi đọc DB queue bảng SELECT FOR UPDATE, throughput Kafka rất cao triệu msg mỗi giây RabbitMQ cao chục nghìn mỗi giây DB queue thấp nghìn mỗi giây, phát lại lịch sử Kafka có giữ log RabbitMQ không DB queue có nếu không xoá, nhiều consumer độc lập Kafka có consumer group RabbitMQ có exchange binding DB queue tự xây, routing phức tạp Kafka đơn giản theo topic RabbitMQ mạnh exchange routing key DB queue tự xây, chi phí vận hành Kafka cao cluster KRaft giám sát RabbitMQ vừa DB queue gần như 0 đã có DB. Cây quyết định có nên dùng Kafka cần throughput rất cao hoặc phát lại hoặc nhiều consumer độc lập trên cùng luồng nếu không vài nghìn job mỗi ngày một consumer dùng DB queue hoặc RabbitMQ gọn hơn nhiều nếu có cần routing phức tạp theo từng message nếu có cân nhắc RabbitMQ routing mạnh hơn nếu không Kafka log scale ngang phát lại

Hình 1: So sánh Kafka với RabbitMQ và hàng đợi trên database. Kafka thắng ở throughput, phát lại và nhiều consumer độc lập; RabbitMQ mạnh về routing phức tạp; hàng đợi DB gọn nhất khi khối lượng nhỏ. Cây quyết định giúp chọn đúng.

Đọc bảng so sánh, ba điểm quyết định:

  • Kafka tỏa sáng khi: cần throughput rất cao (hàng trăm nghìn tới triệu message/giây — bài kafka-01/02), cần phát lại lịch sử (log không xoá — bài kafka-05/08), hoặc có nhiều consumer độc lập đọc cùng một luồng sự kiện (consumer group — bài kafka-03). Đây là những việc queue truyền thống làm khó hoặc không làm được.
  • RabbitMQ hợp hơn khi: cần routing phức tạp theo từng message (exchange, routing key, priority), khối lượng cao nhưng không cực lớn, và không cần phát lại. RabbitMQ là một message broker đúng nghĩa với mô hình định tuyến mạnh mà Kafka (chỉ có topic) không có.
  • Hàng đợi trên database hợp khi: khối lượng nhỏ (vài nghìn job/ngày), một hoặc ít consumer, và bạn đã có database. Một bảng jobs với SELECT ... FOR UPDATE SKIP LOCKED là đủ, chi phí vận hành gần như bằng 0 — không cần dựng cả một cluster Kafka cho một hàng đợi gửi email.

Cây quyết định gói gọn: Có cần throughput rất cao HOẶC phát lại HOẶC nhiều consumer độc lập không? Nếu không — dùng DB queue hoặc RabbitMQ, gọn hơn nhiều. Nếu có — cần routing phức tạp theo message không? Có thì cân nhắc RabbitMQ; không thì Kafka là lựa chọn đúng.

Checklist vận hành và số liệu đã đo

Nếu đã quyết định dùng Kafka, đây là checklist đúc kết từ 11 bài — mỗi mục là một quyết định cấu hình đã được giải thích và đo:

Ảnh chụp nền tối checklist vận hành production đúc kết từ 11 bài đã đo mỗi mục gắn với một bài trong sê-ri chạy qua trước khi đưa Kafka vào production. Checklist cấu hình đúng chọn số partition đủ lớn từ đầu trần song song p2 p3, dùng key có nghĩa để giữ thứ tự theo thực thể p2, RF 3 min.insync.replicas 2 acks all p4 p9, enable.idempotence true mặc định cứ bật p4, commit offset sau khi xử lý xong p5, giám sát consumer lag offset cộng time p6, tune batch.size cộng linger.ms cộng compression p7, retention.ms lớn hơn độ trễ consumer xấu nhất p6 p8, compaction cho topic trạng thái key-value p8, Avro cộng Schema Registry nếu nhiều team p10, outbox pattern khi ghi DB cộng gửi Kafka p11. Số liệu thật đo được qua sê-ri single-node lab ghi 1 partition 527k msg mỗi giây 50 MB mỗi giây p1, ghi 6 partition 1,22 triệu msg mỗi giây 2,1 lần p2, tune batch linger 266 MB mỗi giây 2,25 lần p7, nén lz4 nhỏ 24 lần trên đĩa còn nhanh hơn p7, nén zstd nhỏ 58 lần 26MB còn 460KB p7, Avro 36 byte vs JSON 88 byte 2,4 lần p10, rebalance 3 partition chia cho tối đa 3 consumer p3, compaction 4 value mỗi key giữ 1 mới nhất p8, outbox rollback 0 lệch relay 3 thành 3 msg p11, delivery acks 0 còn chậm hơn acks 1 p4, mọi con số đo trên 1 broker trong Docker cluster thật nhiều broker có đánh đổi độ bền băng thông khác bài p9

Hình 2: Bên trái — checklist cấu hình production, mỗi mục trỏ về bài tương ứng. Bên phải — tổng hợp số liệu thật đo được qua sê-ri (trên single-node lab): throughput, nén, serialization, rebalance, compaction, outbox.

Vài quyết định quan trọng nhất, nhắc lại ngắn gọn:

  • Partition đủ lớn từ đầu (kafka-02/03): partition là trần song song của cả producer lẫn consumer, và đổi số partition sau phá vỡ ánh xạ key. Ước lượng dư một chút ngay từ đầu.
  • Độ bền là bộ ba (kafka-04/09): RF=3 + min.insync.replicas=2 + acks=all, cộng enable.idempotence=true. Thiếu một mắt là có lỗ hổng mất dữ liệu.
  • Commit sau khi xử lý (kafka-05): auto-commit theo đồng hồ có thể mất message; commit thủ công sau xử lý biến rủi ro thành trùng (at-least-once) — cứu được.
  • Giám sát lag (kafka-06): thước đo sức khoẻ số một; cảnh báo khi lag tăng đều không ngừng, và đảm bảo retention.ms lớn hơn độ trễ consumer xấu nhất để lag không biến thành mất dữ liệu.
  • Outbox cho tích hợp DB (kafka-11): đừng dual-write; ghi nghiệp vụ và sự kiện trong một transaction rồi relay.

Và các con số thật nhắc rằng Kafka nhanh: một node ghi 527 nghìn message/giây, 6 partition đạt 1,22 triệu/giây, nén zstd cắt đĩa 58 lần, Avro nhỏ hơn JSON 2,4 lần. Nhưng cũng nhắc phải đo chứ đừng đoán: acks=0 hoá ra còn chậm hơn acks=1, và min.insync.replicas=2 không chặn ghi trên single-node như nhiều người tưởng.

Vài sự thật cần giữ thẳng thắn

Mọi con số trong sê-ri đo trên một broker trong Docker. Chúng cho thấy bản chất cơ chế và trần trên một máy, nhưng cluster production nhiều broker có đánh đổi khác: acks=all thật sự tốn đồng bộ qua mạng (bài kafka-09), replication ngốn băng thông liên-broker, và rebalance trên group lớn đau hơn nhiều. Dùng số của lab để hiểu hướng, benchmark lại trên hạ tầng thật của bạn để lấy con số quyết định dung lượng.

Kafka không phải database, cũng không phải RPC. Một cám dỗ là biến Kafka thành nơi lưu trữ chính (query trực tiếp) hoặc kênh request-response đồng bộ. Cả hai đều lệch mục đích: Kafka là log sự kiện bất đồng bộ. Cần truy vấn linh hoạt thì đọc Kafka đổ vào một store phù hợp; cần request-response thì dùng RPC/HTTP. Dùng đúng vai trò của từng công cụ.

Vận hành Kafka là cam kết dài hạn. Dựng được một cluster Kafka chỉ là khởi đầu; giữ nó khỏe đòi hỏi giám sát lag, cân bằng partition, nâng cấp phiên bản, quản lý retention và dung lượng đĩa, xử lý rebalance. Nếu đội ngũ nhỏ và bài toán không thực sự cần sức mạnh của Kafka, chi phí vận hành này có thể lớn hơn lợi ích — và đó là lý do bảng so sánh ở Hình 1 bắt đầu bằng câu hỏi "bạn có thực sự cần không?".

Ba ý mang về

  1. Chọn công cụ trước, cấu hình sau. Kafka đúng khi cần throughput rất cao, phát lại, hoặc nhiều consumer độc lập; RabbitMQ cho routing phức tạp; hàng đợi DB cho khối lượng nhỏ. Dùng Kafka khi không cần là tự rước gánh nặng vận hành.
  2. Độ bền và đúng đắn là bộ cấu hình gắn kết, không phải một nút. Partition đủ lớn từ đầu, RF=3/min.insync=2/acks=all, idempotence, commit sau xử lý, giám sát lag, retention > lag, compaction cho state, Avro+Registry, outbox — mỗi mục đã được đo và giải thích; bỏ một mục thường mở một lỗ hổng.
  3. Đo chứ đừng đoán, và biết giới hạn của phép đo. Sê-ri này cho số thật (527k/s, 6 partition 2,1×, zstd 58×, Avro 2,4×) nhưng trên single-node Docker — chúng dạy cơ chế, không thay được benchmark trên cluster thật của bạn. Vài trực giác phổ biến (acks=0 nhanh nhất, min.insync chặn mọi lúc) đã bị chính phép đo bác bỏ.

Nguồn