Kafka 1 — Kiến trúc & Khái niệm cốt lõi

13 thg 7, 2026 4 lượt xem
#kafka
#kien-truc
#replication
#partition

Kafka là gì

Apache Kafka là một nền tảng streaming phân tán (distributed streaming platform), mà bản chất cốt lõi là một commit log phân tán (distributed commit log). Thay vì coi Kafka đơn thuần như một "hàng đợi thông điệp" (message queue), cách nhìn chính xác hơn là: Kafka duy trì một cấu trúc log append-only — chỉ ghi thêm vào cuối, không sửa, không chèn giữa — và phân tán nó qua nhiều máy chủ để đạt độ bền, khả năng chịu lỗi và thông lượng cao.

Kafka cung cấp ba năng lực chính:

  • Publish–subscribe bền vững: producer ghi bản ghi (record) vào Kafka, consumer đọc lại. Điểm khác biệt then chốt so với message queue truyền thống là dữ liệu không bị xoá khi đã đọc — nó được lưu theo chính sách retention (thời gian hoặc dung lượng).
  • Lưu trữ (storage): Kafka lưu dữ liệu trên đĩa một cách bền vững và có nhân bản (replication), nên có thể coi như một hệ thống lưu trữ log phân tán, không chỉ là kênh truyền tải.
  • Xử lý luồng (stream processing): qua Kafka Streams / ksqlDB, dữ liệu có thể được biến đổi ngay trong hệ sinh thái Kafka.

Chính mô hình log bất biến + retention là điều làm nên các đặc tính đặc trưng của Kafka: nhiều consumer độc lập cùng đọc một luồng, và khả năng replay (đọc lại từ quá khứ) bằng cách tua lại offset.

Broker & Cluster

Một broker là một tiến trình Kafka chạy trên một máy chủ (một node). Broker chịu trách nhiệm:

  • Nhận bản ghi từ producer và ghi xuống đĩa.
  • Phục vụ yêu cầu đọc (fetch) từ consumer.
  • Giữ và nhân bản dữ liệu của các partition được phân công cho nó.

Nhiều broker hợp thành một cluster. Mỗi broker có một broker.id duy nhất. Trong một cluster, dữ liệu của các topic được phân tán (spread) trên nhiều broker để chia tải và nhân bản để chịu lỗi.

Trong cluster luôn có một broker đóng vai trò controller (mô tả ở phần sau) quản lý metadata và điều phối; các broker còn lại là broker thường nhưng vẫn phục vụ dữ liệu bình thường.

Topic & Partition

Topic

Một topic là tên logic của một luồng bản ghi — ví dụ orders, payments, clickstream. Topic là khái niệm phân loại; producer ghi vào topic, consumer đăng ký topic. Bản thân topic không lưu dữ liệu trực tiếp — nó được chia thành các partition.

Partition — đơn vị song song và thứ tự

Mỗi topic gồm một hoặc nhiều partition. Partition là đơn vị cơ bản của Kafka, và hiểu đúng partition là chìa khoá để hiểu toàn bộ hệ thống. Partition có hai vai trò cốt lõi:

  1. Đơn vị song song (parallelism): mỗi partition có thể được ghi và đọc độc lập. Số partition quyết định mức độ song song tối đa của một consumer group — vì trong một group, mỗi partition chỉ được gán cho đúng một consumer tại một thời điểm.
  2. Đơn vị thứ tự (ordering): Kafka chỉ đảm bảo thứ tự trong phạm vi một partition, không đảm bảo thứ tự giữa các partition. Đây là điểm mà rất nhiều người hiểu sai.

Offset và log append-only bất biến

Mỗi partition là một log append-only bất biến: bản ghi mới luôn được nối vào cuối. Mỗi bản ghi trong partition được gán một số nguyên tăng dần, đơn điệu, gọi là offset. Offset là cục bộ trong từng partition (partition 0 có offset 0,1,2… riêng, partition 1 cũng có offset 0,1,2… riêng — chúng không liên quan nhau).

Đặc tính của offset và log:

  • Bản ghi một khi đã ghi thì bất biến (immutable): Kafka không sửa hay xoá bản ghi lẻ; chỉ có retention xoá cả log segment cũ.
  • Consumer tự quản lý vị trí đọc bằng cách commit offset. Việc "đã đọc" hoàn toàn do consumer theo dõi, không phải broker đánh dấu. Nhờ vậy nhiều consumer group độc lập cùng đọc một partition ở các offset khác nhau.
  • Muốn đọc lại (replay): chỉ cần seek về một offset cũ (hoặc theo timestamp).

Key → Partition: hashing và ảnh hưởng đến thứ tự

Khi producer gửi một bản ghi, nó chọn partition đích theo quy tắc:

  • Có key: partition được xác định bằng hash(key) % số_partition (mặc định dùng murmur2 hashing). Cùng một key luôn rơi vào cùng một partition (miễn là số partition không đổi).
  • Không có key: dùng chiến lược phân phối theo lô (sticky partitioning ở phiên bản mới) để trải đều tải, thứ tự giữa các bản ghi khác nhau không đảm bảo.

Đây là lý do key ảnh hưởng trực tiếp đến thứ tự: vì Kafka chỉ đảm bảo thứ tự trong một partition, nên nếu bạn cần các sự kiện của cùng một thực thể (ví dụ cùng một customer_id) được xử lý theo đúng thứ tự, bạn phải đặt customer_id làm key. Khi đó mọi sự kiện của khách hàng đó vào chung một partition, và consumer sẽ đọc chúng theo đúng trình tự offset.

Hệ quả cần lưu ý:

  • Chọn key phân bố lệch (skewed) sẽ tạo partition nóng (hot partition) — một partition nhận quá nhiều lưu lượng, phá vỡ cân bằng tải.
  • Thay đổi số partition sau này sẽ làm hàm băm ánh xạ khác đi, nên các bản ghi cùng key mới có thể vào partition khác — làm mất đảm bảo thứ tự "toàn cục theo key" xuyên qua thời điểm thay đổi. Vì thế số partition thường được lên kế hoạch cẩn thận từ đầu.

Replication: leader, follower, ISR

Để chịu lỗi, mỗi partition được nhân bản thành nhiều replica. Số bản sao gọi là replication factor (RF). Ví dụ RF = 3 nghĩa là mỗi partition có 3 bản trên 3 broker khác nhau.

Trong các replica của một partition:

  • Một replica là leader: mọi thao tác đọc/ghi của partition đó đều đi qua leader.
  • Các replica còn lại là follower: chúng liên tục fetch (kéo) dữ liệu từ leader để đồng bộ. Follower không phục vụ producer/consumer trực tiếp (trong mô hình cổ điển).

ISR — In-Sync Replicas

ISR (In-Sync Replicas) là tập hợp các replica (gồm leader và những follower) đang theo kịp leader trong ngưỡng cho phép (replica.lag.time.max.ms). Một follower tụt lại quá lâu sẽ bị loại khỏi ISR; khi bắt kịp lại thì được đưa vào lại.

ISR quan trọng vì hai lý do:

  1. Bầu leader mới: khi leader chết, controller chọn leader mới từ ISR (mặc định), đảm bảo không mất dữ liệu đã được xác nhận. Nếu cho phép bầu từ ngoài ISR (unclean.leader.election.enable=true) thì có nguy cơ mất dữ liệu — thường tắt trong môi trường cần độ bền cao.
  2. Ngưỡng ghi bền vững: cấu hình min.insync.replicas xác định số replica tối thiểu phải nằm trong ISR để một lần ghi với acks=all được coi là thành công.

Liên hệ với acks (producer)

Producer điều khiển độ bền qua tham số acks:

acksÝ nghĩaĐộ bềnĐộ trễ
0Không chờ xác nhậnThấp nhất, có thể mất dữ liệuThấp nhất
1Chờ leader ghi xongTrung bình, mất nếu leader chết trước khi follower syncTrung bình
all (-1)Chờ tất cả ISR xác nhậnCao nhấtCao hơn

Kết hợp thường dùng cho độ bền cao: acks=all + min.insync.replicas=2 + RF=3. Khi đó, dù mất một broker, dữ liệu đã xác nhận vẫn an toàn, và cluster vẫn ghi được (còn ít nhất 2 replica trong ISR). Chi tiết producer sẽ nói kỹ ở bài sau.

Controller & Metadata

Trong mỗi cluster có một broker được bầu làm controller. Controller chịu trách nhiệm quản lý metadata của cluster và điều phối:

  • Theo dõi broker nào còn sống, partition nào ở đâu.
  • Quản lý danh sách ISR của từng partition.
  • Bầu leader cho partition khi leader hiện tại không còn khả dụng.
  • Truyền phát các thay đổi metadata đến các broker khác.

Nếu controller chết, một broker khác sẽ được bầu lên thay thế. Điểm khác biệt lớn giữa hai thế hệ Kafka nằm ở chỗ metadata này được lưu và đồng thuận ở đâu.

KRaft vs ZooKeeper

Kiến trúc ZooKeeper cũ

Trước đây, Kafka phụ thuộc vào một cụm Apache ZooKeeper riêng biệt để lưu metadata cluster (danh sách broker, cấu hình topic, ACL) và để bầu controller. Nhược điểm:

  • Phải vận hành hai hệ thống phân tán (Kafka + ZooKeeper), tăng độ phức tạp và điểm lỗi.
  • Metadata được đồng bộ qua ZooKeeper rồi mới lan ra broker, tạo độ trễ và giới hạn quy mô (số partition tối đa bị chặn bởi khả năng của ZooKeeper).
  • Quá trình controller failover có thể chậm khi metadata lớn.

KRaft — Kafka Raft

KRaft (Kafka Raft) loại bỏ hoàn toàn ZooKeeper. Kafka tự quản lý metadata bằng giao thức đồng thuận Raft ngay trong nội bộ Kafka:

  • Metadata được lưu như một log nội bộ (__cluster_metadata topic), chính là mô hình log mà Kafka vốn giỏi.
  • Một nhóm broker đóng vai trò controller (controller quorum) chạy Raft để đồng thuận metadata; một trong số đó là active controller.
  • Không còn phụ thuộc ngoài; failover nhanh hơn, quy mô partition cao hơn nhiều, vận hành đơn giản hơn (một hệ thống duy nhất).

KRaft đã đạt production-ready từ Kafka 3.3 và trở thành mặc định; ZooKeeper bị loại bỏ trong Kafka 4.0. Ngày nay khi triển khai mới, nên dùng KRaft.

Tiêu chíZooKeeper (cũ)KRaft (mới)
Nơi lưu metadataCụm ZooKeeper ngoàiLog nội bộ Kafka
Đồng thuậnZAB (ZooKeeper)Raft
Số hệ thống vận hành2 (Kafka + ZK)1 (chỉ Kafka)
Controller1 broker bầu qua ZKController quorum
FailoverChậm hơn khi metadata lớnNhanh
Quy mô partitionGiới hạn bởi ZKCao hơn nhiều

Log Segment & Retention (giới thiệu)

Về mặt vật lý, log của mỗi partition không phải một file khổng lồ mà được chia thành các segment — mỗi segment gồm một file dữ liệu (.log) cùng các file chỉ mục (.index theo offset, .timeindex theo thời gian). Segment đang được ghi gọi là active segment; khi đầy (theo segment.bytes) hoặc hết thời gian (segment.ms), nó được đóng và mở segment mới.

Retention quyết định giữ dữ liệu bao lâu:

  • Theo thời gian: retention.ms (ví dụ giữ 7 ngày).
  • Theo dung lượng: retention.bytes (giữ tối đa X byte mỗi partition).
  • Kafka xoá theo cả segment, không xoá từng bản ghi lẻ — nên retention làm việc ở mức segment.

Ngoài chính sách xoá (delete), Kafka còn có log compaction (cleanup.policy=compact) giữ lại bản ghi mới nhất cho mỗi key — hữu ích cho các topic dạng "trạng thái mới nhất". Chi tiết về storage internals sẽ được đào sâu ở bài riêng.

Vì sao Kafka đạt thông lượng cao

Kafka nổi tiếng nhanh nhờ vài quyết định thiết kế bám sát bản chất "log tuần tự":

  • Sequential I/O (ghi/đọc tuần tự): vì log là append-only, Kafka ghi tuần tự xuống đĩa. Ghi tuần tự trên đĩa (kể cả HDD) nhanh hơn ghi ngẫu nhiên rất nhiều lần, và tận dụng tốt page cache của HĐH.
  • Zero-copy: khi phục vụ consumer, Kafka dùng sendfile để truyền dữ liệu từ page cache thẳng ra network socket, không sao chép qua bộ nhớ ứng dụng — giảm CPU và context switch.
  • Batching (gộp lô): producer gộp nhiều bản ghi thành một batch trước khi gửi; broker lưu và consumer đọc cũng theo lô. Batch giảm chi phí cố định mỗi request và tăng hiệu quả nén.
  • Nén (compression): batch được nén (gzip, snappy, lz4, zstd) end-to-end, giảm băng thông mạng và dung lượng đĩa.
  • Tận dụng OS page cache: Kafka giao phần lớn caching cho HĐH thay vì cache trong JVM heap, tránh áp lực GC và tận dụng bộ nhớ hệ thống.

Các yếu tố này cộng hưởng: dữ liệu vừa ghi thường vẫn nằm trong page cache, nên consumer bắt kịp đọc gần như từ RAM chứ không phải đĩa.

Kafka so với message queue truyền thống

Khía cạnhMessage queue truyền thốngKafka
Mô hình lấy dữ liệuBroker push cho consumerConsumer pull (fetch) theo nhịp của mình
Vòng đời thông điệpThường xoá khi đã tiêu thụGiữ theo retention, không xoá khi đọc
Đọc lại (replay)Khó/không hỗ trợDễ, chỉ cần seek offset
Nhiều nhóm tiêu thụCạnh tranh cùng một hàng đợiNhiều consumer group đọc độc lập cùng luồng
Thứ tựTuỳ hệ, thường yếu khi scaleĐảm bảo trong từng partition
Lưu trữBộ đệm tạm thờiLog bền vững, nhân bản

Điểm cần nhấn mạnh:

  • Pull thay vì push: consumer tự kéo dữ liệu, tự kiểm soát tốc độ (backpressure tự nhiên), tránh bị broker đẩy quá tải.
  • Retention + replay: vì dữ liệu không mất khi đọc, ta có thể thêm consumer mới đọc lại lịch sử, tái xử lý sau khi sửa bug, hoặc phục vụ nhiều hệ downstream với cùng nguồn sự thật.

Vì vậy Kafka phù hợp cho event streaming / event sourcing / tích hợp dữ liệu hơn là chỉ đóng vai một task queue thuần.

Một vài lệnh CLI minh hoạ

Các lệnh dưới đây chỉ để minh hoạ khái niệm (không chạy trong sandbox Postgres):

# Tạo topic với 6 partition, replication factor 3
kafka-topics.sh --bootstrap-server localhost:9092 \
  --create --topic orders --partitions 6 --replication-factor 3

# Xem chi tiết topic: leader, replicas, ISR của từng partition
kafka-topics.sh --bootstrap-server localhost:9092 \
  --describe --topic orders

# Ghi thử vài bản ghi
kafka-console-producer.sh --bootstrap-server localhost:9092 --topic orders

# Đọc từ đầu (thể hiện khả năng replay)
kafka-console-consumer.sh --bootstrap-server localhost:9092 \
  --topic orders --from-beginning

Lệnh --describe cho thấy đúng những khái niệm đã học: mỗi partition có một Leader, danh sách Replicas và danh sách Isr — đây là cách kiểm tra sức khoẻ replication trong thực tế.

Tóm tắt

  • Kafka là một commit log phân tán / nền tảng streaming, publish–subscribe bền vững với retention và replay, không phải message queue truyền thống.
  • Đơn vị cốt lõi là partition: một log append-only bất biến, đánh offset tăng dần cục bộ; là đơn vị song song và là phạm vi duy nhất đảm bảo thứ tự.
  • Key → partition qua hashing quyết định cùng key đi cùng partition, nên key kiểm soát thứ tự và có thể gây hot partition nếu lệch.
  • Replication với leader/follower và ISR đảm bảo chịu lỗi; kết hợp acks=all + min.insync.replicas + RF cho độ bền cao.
  • Controller quản lý metadata và bầu leader; KRaft thay ZooKeeper bằng Raft nội bộ, đơn giản và mở rộng tốt hơn.
  • Thông lượng cao nhờ sequential I/O, zero-copy, batching, nén, page cache.

Tự kiểm tra

  1. Vì sao Kafka chỉ đảm bảo thứ tự trong phạm vi một partition mà không phải toàn topic? Điều này ảnh hưởng thế nào đến việc chọn key?
  2. Giải thích vai trò của ISR và mối liên hệ giữa acks=all, min.insync.replicas và replication factor trong việc đảm bảo không mất dữ liệu.
  3. Offset được đánh cục bộ theo partition có ý nghĩa gì với việc nhiều consumer group cùng đọc một topic và với khả năng replay?
  4. So sánh KRaft và kiến trúc ZooKeeper cũ: metadata lưu ở đâu, đồng thuận bằng gì, và vì sao KRaft mở rộng tốt hơn?
  5. Kể tên ít nhất ba cơ chế giúp Kafka đạt thông lượng cao và giải thích ngắn gọn từng cơ chế.
  6. Tại sao mô hình pull + retention của Kafka lại phù hợp với event streaming hơn so với message queue push truyền thống?

Đọc tiếp

Kafka 2 — Producer chuyên sâu

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.

13 thg 7, 2026 8

Vì sao xử lý phân tán, mô hình Spark (RDD/DataFrame), lazy evaluation, shuffle và tối ưu.

13 thg 7, 2026 5

Data Engineering là gì, vai trò trong vòng đời dữ liệu, và bức tranh hệ sinh thái công cụ.

13 thg 7, 2026 5

Đả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.

13 thg 7, 2026 5